diff options
Diffstat (limited to 'lib/src/main/java')
23 files changed, 808 insertions, 383 deletions
diff --git a/lib/src/main/java/com/trilead/ssh2/Connection.java b/lib/src/main/java/com/trilead/ssh2/Connection.java index acc8395..38b96c5 100644 --- a/lib/src/main/java/com/trilead/ssh2/Connection.java +++ b/lib/src/main/java/com/trilead/ssh2/Connection.java @@ -15,6 +15,7 @@ import com.trilead.ssh2.channel.ChannelManager;  import com.trilead.ssh2.crypto.CryptoWishList;
  import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
  import com.trilead.ssh2.crypto.digest.MAC;
 +import com.trilead.ssh2.log.Logger;
  import com.trilead.ssh2.packets.PacketIgnore;
  import com.trilead.ssh2.transport.KexManager;
  import com.trilead.ssh2.transport.TransportManager;
 @@ -29,13 +30,16 @@ import com.trilead.ssh2.util.TimeoutService.TimeoutToken;   * <ol>
   * <li>creates a {@link #Connection(String) Connection} object.</li>
   * <li>calls the {@link #connect() connect()} method.</li>
 - * <li>calls some of the authentication methods (e.g., {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
 - * <li>calls one or several times the {@link #openSession() openSession()} method.</li>
 - * <li>finally, one must close the connection and release resources with the {@link #close() close()} method.</li>
 + * <li>calls some of the authentication methods (e.g.,
 + * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
 + * <li>calls one or several times the {@link #openSession() openSession()}
 + * method.</li>
 + * <li>finally, one must close the connection and release resources with the
 + * {@link #close() close()} method.</li>
   * </ol>
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: Connection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: Connection.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
   */
  public class Connection
 @@ -43,12 +47,12 @@ public class Connection  	/**
  	 * The identifier presented to the SSH-2 server.
  	 */
 -	public final static String identification = "TrileadSSH2Java_211";
 +	public final static String identification = "TrileadSSH2Java_213";
 -	/* Will be used to generate all random data needed for the current connection.
 -	 * Note: SecureRandom.nextBytes() is thread safe.
 +	/**
 +	 * Will be used to generate all random data needed for the current
 +	 * connection. Note: SecureRandom.nextBytes() is thread safe.
  	 */
 -
  	private SecureRandom generator;
  	/**
 @@ -74,7 +78,8 @@ public class Connection  	/**
  	 * Unless you know what you are doing, you will never need this.
  	 * 
 -	 * @return The list of supported server host key algorthims by this implementation.
 +	 * @return The list of supported server host key algorthims by this
 +	 *         implementation.
  	 */
  	public static synchronized String[] getAvailableServerHostKeyAlgorithms()
  	{
 @@ -106,9 +111,10 @@ public class Connection  	 * Prepares a fresh <code>Connection</code> object which can then be used
  	 * to establish a connection to the specified SSH-2 server.
  	 * <p>
 -	 * Same as {@link #Connection(String, int) Connection(hostname, 22)}. 
 +	 * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
  	 * 
 -	 * @param hostname the hostname of the SSH-2 server.
 +	 * @param hostname
 +	 *            the hostname of the SSH-2 server.
  	 */
  	public Connection(String hostname)
  	{
 @@ -156,9 +162,10 @@ public class Connection  	 * @return whether the connection is now authenticated.
  	 * @throws IOException
  	 * 
 -	 * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
 -	 * 		      methods, this method is just a wrapper for it and will
 -	 *            disappear in future builds.
 +	 * @deprecated You should use one of the
 +	 *             {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
 +	 *             methods, this method is just a wrapper for it and will
 +	 *             disappear in future builds.
  	 * 
  	 */
  	public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
 @@ -187,8 +194,10 @@ public class Connection  	}
  	/**
 -	 * A wrapper that calls {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
 -	 * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod list.
 +	 * A wrapper that calls
 +	 * {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
 +	 * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod
 +	 * list.
  	 * 
  	 * @param user
  	 *            A <code>String</code> holding the username.
 @@ -276,12 +285,14 @@ public class Connection  	 * the remaining possible methods).
  	 * <p>
  	 * Note: if this method fails, then please double-check that it is actually
 -	 * offered by the server (use {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
 +	 * offered by the server (use
 +	 * {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
  	 * <p>
 -	 * Often, password authentication is disabled, but users are not aware of it.
 -	 * Many servers only offer "publickey" and "keyboard-interactive". However,
 -	 * even though "keyboard-interactive" *feels* like password authentication
 -	 * (e.g., when using the putty or openssh clients) it is *not* the same mechanism.
 +	 * Often, password authentication is disabled, but users are not aware of
 +	 * it. Many servers only offer "publickey" and "keyboard-interactive".
 +	 * However, even though "keyboard-interactive" *feels* like password
 +	 * authentication (e.g., when using the putty or openssh clients) it is
 +	 * *not* the same mechanism.
  	 * 
  	 * @param user
  	 * @param password
 @@ -314,26 +325,26 @@ public class Connection  	}
  	/**
 -	 * After a successful connect, one has to authenticate oneself.
 -	 * This method can be used to explicitly use the special "none"
 -	 * authentication method (where only a username has to be specified).
 +	 * After a successful connect, one has to authenticate oneself. This method
 +	 * can be used to explicitly use the special "none" authentication method
 +	 * (where only a username has to be specified).
  	 * <p>
  	 * Note 1: The "none" method may always be tried by clients, however as by
  	 * the specs, the server will not explicitly announce it. In other words,
  	 * the "none" token will never show up in the list returned by
  	 * {@link #getRemainingAuthMethods(String)}.
  	 * <p>
 -	 * Note 2: no matter which one of the authenticateWithXXX() methods
 -	 * you call, the library will always issue exactly one initial "none"
 +	 * Note 2: no matter which one of the authenticateWithXXX() methods you
 +	 * call, the library will always issue exactly one initial "none"
  	 * authentication request to retrieve the initially allowed list of
  	 * authentication methods by the server. Please read RFC 4252 for the
  	 * details.
  	 * <p>
  	 * If the authentication phase is complete, <code>true</code> will be
  	 * returned. If further authentication steps are needed, <code>false</code>
 -	 * is returned and one can retry by any other authentication method
 -	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
 -	 * the remaining possible methods).
 +	 * is returned and one can retry by any other authentication method (use the
 +	 * <code>getRemainingAuthMethods</code> method to get a list of the
 +	 * remaining possible methods).
  	 * 
  	 * @param user
  	 * @return if the connection is now authenticated.
 @@ -357,7 +368,7 @@ public class Connection  			throw new IllegalArgumentException("user argument is null");
  		/* Trigger the sending of the PacketUserauthRequestNone packet */
 -		/* (if not already done)                                       */
 +		/* (if not already done) */
  		authenticated = am.authenticateNone(user);
 @@ -365,18 +376,19 @@ public class Connection  	}
  	/**
 -	 * After a successful connect, one has to authenticate oneself.
 -	 * The authentication method "publickey" works by signing a challenge
 -	 * sent by the server. The signature is either DSA or RSA based - it
 -	 * just depends on the type of private key you specify, either a DSA
 -	 * or RSA private key in PEM format. And yes, this is may seem to be a
 -	 * little confusing, the method is called "publickey" in the SSH-2 protocol
 -	 * specification, however since we need to generate a signature, you
 -	 * actually have to supply a private key =).
 +	 * After a successful connect, one has to authenticate oneself. The
 +	 * authentication method "publickey" works by signing a challenge sent by
 +	 * the server. The signature is either DSA or RSA based - it just depends on
 +	 * the type of private key you specify, either a DSA or RSA private key in
 +	 * PEM format. And yes, this is may seem to be a little confusing, the
 +	 * method is called "publickey" in the SSH-2 protocol specification, however
 +	 * since we need to generate a signature, you actually have to supply a
 +	 * private key =).
  	 * <p>
 -	 * The private key contained in the PEM file may also be encrypted ("Proc-Type: 4,ENCRYPTED").
 -	 * The library supports DES-CBC and DES-EDE3-CBC encryption, as well
 -	 * as the more exotic PEM encrpytions AES-128-CBC, AES-192-CBC and AES-256-CBC.
 +	 * The private key contained in the PEM file may also be encrypted
 +	 * ("Proc-Type: 4,ENCRYPTED"). The library supports DES-CBC and DES-EDE3-CBC
 +	 * encryption, as well as the more exotic PEM encrpytions AES-128-CBC,
 +	 * AES-192-CBC and AES-256-CBC.
  	 * <p>
  	 * If the authentication phase is complete, <code>true</code> will be
  	 * returned. If the server does not accept the request (or if further
 @@ -385,23 +397,24 @@ public class Connection  	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
  	 * the remaining possible methods).
  	 * <p>
 -	 * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
 -	 * it is not in the expected format. You have to convert it to the OpenSSH
 -	 * key format by using the "puttygen" tool (can be downloaded from the Putty
 -	 * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
 -	 * functionality to get a proper PEM file.
 +	 * NOTE PUTTY USERS: Event though your key file may start with
 +	 * "-----BEGIN..." it is not in the expected format. You have to convert it
 +	 * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
 +	 * from the Putty website). Simply load your key and then use the
 +	 * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
  	 * 
  	 * @param user
  	 *            A <code>String</code> holding the username.
  	 * @param pemPrivateKey
 -	 *            A <code>char[]</code> containing a DSA or RSA private key of the
 -	 *            user in OpenSSH key format (PEM, you can't miss the
 -	 *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
 -	 *            tag). The char array may contain linebreaks/linefeeds.
 +	 *            A <code>char[]</code> containing a DSA or RSA private key of
 +	 *            the user in OpenSSH key format (PEM, you can't miss the
 +	 *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE
 +	 *            KEY-----" tag). The char array may contain
 +	 *            linebreaks/linefeeds.
  	 * @param password
 -	 *            If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then
 -	 *            you must specify a password. Otherwise, this argument will be ignored
 -	 *            and can be set to <code>null</code>.
 +	 *            If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED")
 +	 *            then you must specify a password. Otherwise, this argument
 +	 *            will be ignored and can be set to <code>null</code>.
  	 * 
  	 * @return whether the connection is now authenticated.
  	 * @throws IOException
 @@ -433,25 +446,27 @@ public class Connection  	}
  	/**
 -	 * A convenience wrapper function which reads in a private key (PEM format, either DSA or RSA)
 -	 * and then calls <code>authenticateWithPublicKey(String, char[], String)</code>.
 +	 * A convenience wrapper function which reads in a private key (PEM format,
 +	 * either DSA or RSA) and then calls
 +	 * <code>authenticateWithPublicKey(String, char[], String)</code>.
  	 * <p>
 -	 * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
 -	 * it is not in the expected format. You have to convert it to the OpenSSH
 -	 * key format by using the "puttygen" tool (can be downloaded from the Putty
 -	 * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
 -	 * functionality to get a proper PEM file.
 +	 * NOTE PUTTY USERS: Event though your key file may start with
 +	 * "-----BEGIN..." it is not in the expected format. You have to convert it
 +	 * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
 +	 * from the Putty website). Simply load your key and then use the
 +	 * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
  	 * 
  	 * @param user
  	 *            A <code>String</code> holding the username.
  	 * @param pemFile
 -	 *            A <code>File</code> object pointing to a file containing a DSA or RSA
 -	 *            private key of the user in OpenSSH key format (PEM, you can't miss the
 -	 *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
 -	 *            tag).
 +	 *            A <code>File</code> object pointing to a file containing a
 +	 *            DSA or RSA private key of the user in OpenSSH key format (PEM,
 +	 *            you can't miss the "-----BEGIN DSA PRIVATE KEY-----" or
 +	 *            "-----BEGIN RSA PRIVATE KEY-----" tag).
  	 * @param password
 -	 *            If the PEM file is encrypted then you must specify the password.
 -	 *            Otherwise, this argument will be ignored and can be set to <code>null</code>.
 +	 *            If the PEM file is encrypted then you must specify the
 +	 *            password. Otherwise, this argument will be ignored and can be
 +	 *            set to <code>null</code>.
  	 * 
  	 * @return whether the connection is now authenticated.
  	 * @throws IOException
 @@ -482,17 +497,19 @@ public class Connection  	}
  	/**
 -	 * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any time,
 -	 * but it is best to add connection monitors before invoking
 -	 * <code>connect()</code> to avoid glitches (e.g., you add a connection monitor after
 -	 * a successful connect(), but the connection has died in the mean time. Then,
 -	 * your connection monitor won't be notified.) 
 +	 * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any
 +	 * time, but it is best to add connection monitors before invoking
 +	 * <code>connect()</code> to avoid glitches (e.g., you add a connection
 +	 * monitor after a successful connect(), but the connection has died in the
 +	 * mean time. Then, your connection monitor won't be notified.)
  	 * <p>
  	 * You can add as many monitors as you like.
  	 * 
  	 * @see ConnectionMonitor
  	 * 
 -	 * @param cmon An object implementing the <code>ConnectionMonitor</code> interface.
 +	 * @param cmon
 +	 *            An object implementing the <code>ConnectionMonitor</code>
 +	 *            interface.
  	 */
  	public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
  	{
 @@ -533,9 +550,12 @@ public class Connection  	}
  	/**
 -	 * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
 +	 * Same as
 +	 * {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
  	 * 
 -	 * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
 +	 * @return see comments for the
 +	 *         {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
 +	 *         method.
  	 * @throws IOException
  	 */
  	public synchronized ConnectionInfo connect() throws IOException
 @@ -544,9 +564,12 @@ public class Connection  	}
  	/**
 -	 * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
 +	 * Same as
 +	 * {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
  	 * 
 -	 * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
 +	 * @return see comments for the
 +	 *         {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
 +	 *         method.
  	 * @throws IOException
  	 */
  	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
 @@ -556,74 +579,85 @@ public class Connection  	/**
  	 * Connect to the SSH-2 server and, as soon as the server has presented its
 -	 * host key, use the {@link ServerHostKeyVerifier#verifyServerHostKey(String,
 -	 * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}
 -	 * method of the <code>verifier</code> to ask for permission to proceed.
 -	 * If <code>verifier</code> is <code>null</code>, then any host key will be
 -	 * accepted - this is NOT recommended, since it makes man-in-the-middle attackes
 -	 * VERY easy (somebody could put a proxy SSH server between you and the real server).
 +	 * host key, use the
 +	 * {@link ServerHostKeyVerifier#verifyServerHostKey(String, int, String,
 +	 * byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method of the
 +	 * <code>verifier</code> to ask for permission to proceed. If
 +	 * <code>verifier</code> is <code>null</code>, then any host key will
 +	 * be accepted - this is NOT recommended, since it makes man-in-the-middle
 +	 * attackes VERY easy (somebody could put a proxy SSH server between you and
 +	 * the real server).
  	 * <p>
  	 * Note: The verifier will be called before doing any crypto calculations
 -	 * (i.e., diffie-hellman). Therefore, if you don't like the presented host key then
 -	 * no CPU cycles are wasted (and the evil server has less information about us).
 +	 * (i.e., diffie-hellman). Therefore, if you don't like the presented host
 +	 * key then no CPU cycles are wasted (and the evil server has less
 +	 * information about us).
  	 * <p>
 -	 * However, it is still possible that the server presented a fake host key: the server
 -	 * cheated (typically a sign for a man-in-the-middle attack) and is not able to generate
 -	 * a signature that matches its host key. Don't worry, the library will detect such
 -	 * a scenario later when checking the signature (the signature cannot be checked before
 -	 * having completed the diffie-hellman exchange).
 +	 * However, it is still possible that the server presented a fake host key:
 +	 * the server cheated (typically a sign for a man-in-the-middle attack) and
 +	 * is not able to generate a signature that matches its host key. Don't
 +	 * worry, the library will detect such a scenario later when checking the
 +	 * signature (the signature cannot be checked before having completed the
 +	 * diffie-hellman exchange).
  	 * <p>
 -	 * Note 2: The  {@link ServerHostKeyVerifier#verifyServerHostKey(String,
 -	 * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method
 -	 * will *NOT* be called from the current thread, the call is being made from a
 +	 * Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String, int,
 +	 * String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method will
 +	 * *NOT* be called from the current thread, the call is being made from a
  	 * background thread (there is a background dispatcher thread for every
 -	 * established connection). 
 +	 * established connection).
  	 * <p>
 -	 * Note 3: This method will block as long as the key exchange of the underlying connection
 -	 * has not been completed (and you have not specified any timeouts).
 +	 * Note 3: This method will block as long as the key exchange of the
 +	 * underlying connection has not been completed (and you have not specified
 +	 * any timeouts).
  	 * <p>
 -	 * Note 4: If you want to re-use a connection object that was successfully connected,
 -	 * then you must call the {@link #close()} method before invoking <code>connect()</code> again.
 +	 * Note 4: If you want to re-use a connection object that was successfully
 +	 * connected, then you must call the {@link #close()} method before invoking
 +	 * <code>connect()</code> again.
  	 * 
  	 * @param verifier
 -	 *            An object that implements the
 -	 *            {@link ServerHostKeyVerifier} interface. Pass <code>null</code>
 -	 *            to accept any server host key - NOT recommended.
 -	 *            
 +	 *            An object that implements the {@link ServerHostKeyVerifier}
 +	 *            interface. Pass <code>null</code> to accept any server host
 +	 *            key - NOT recommended.
 +	 * 
  	 * @param connectTimeout
 -	 *            Connect the underlying TCP socket to the server with the given timeout
 -	 *            value (non-negative, in milliseconds). Zero means no timeout. If a proxy is being
 -	 *            used (see {@link #setProxyData(ProxyData)}), then this timeout is used for the
 -	 *            connection establishment to the proxy.
 +	 *            Connect the underlying TCP socket to the server with the given
 +	 *            timeout value (non-negative, in milliseconds). Zero means no
 +	 *            timeout. If a proxy is being used (see
 +	 *            {@link #setProxyData(ProxyData)}), then this timeout is used
 +	 *            for the connection establishment to the proxy.
  	 * 
  	 * @param kexTimeout
  	 *            Timeout for complete connection establishment (non-negative,
 -	 *            in milliseconds). Zero means no timeout. The timeout counts from the
 -	 *            moment you invoke the connect() method and is cancelled as soon as the
 -	 *            first key-exchange round has finished. It is possible that
 -	 *            the timeout event will be fired during the invocation of the
 -	 *            <code>verifier</code> callback, but it will only have an effect after
 -	 *            the <code>verifier</code> returns.
 -	 *            
 -	 * @return A {@link ConnectionInfo} object containing the details of
 -	 *            the established connection.
 -	 *         
 +	 *            in milliseconds). Zero means no timeout. The timeout counts
 +	 *            from the moment you invoke the connect() method and is
 +	 *            cancelled as soon as the first key-exchange round has
 +	 *            finished. It is possible that the timeout event will be fired
 +	 *            during the invocation of the <code>verifier</code> callback,
 +	 *            but it will only have an effect after the
 +	 *            <code>verifier</code> returns.
 +	 * 
 +	 * @return A {@link ConnectionInfo} object containing the details of the
 +	 *         established connection.
 +	 * 
  	 * @throws IOException
 -	 *            If any problem occurs, e.g., the server's host key is not
 -	 *            accepted by the <code>verifier</code> or there is problem during
 -	 *            the initial crypto setup (e.g., the signature sent by the server is wrong).
 -	 *            <p>
 -	 *            In case of a timeout (either connectTimeout or kexTimeout)
 -	 *            a SocketTimeoutException is thrown.
 -	 *            <p>
 -	 *            An exception may also be thrown if the connection was already successfully
 -	 *            connected (no matter if the connection broke in the mean time) and you invoke
 -	 *            <code>connect()</code> again without having called {@link #close()} first.
 -	 *            <p>
 -	 *            If a HTTP proxy is being used and the proxy refuses the connection,
 -	 *            then a {@link HTTPProxyException} may be thrown, which
 -	 *            contains the details returned by the proxy. If the proxy is buggy and does
 -	 *            not return a proper HTTP response, then a normal IOException is thrown instead.        
 +	 *             If any problem occurs, e.g., the server's host key is not
 +	 *             accepted by the <code>verifier</code> or there is problem
 +	 *             during the initial crypto setup (e.g., the signature sent by
 +	 *             the server is wrong).
 +	 *             <p>
 +	 *             In case of a timeout (either connectTimeout or kexTimeout) a
 +	 *             SocketTimeoutException is thrown.
 +	 *             <p>
 +	 *             An exception may also be thrown if the connection was already
 +	 *             successfully connected (no matter if the connection broke in
 +	 *             the mean time) and you invoke <code>connect()</code> again
 +	 *             without having called {@link #close()} first.
 +	 *             <p>
 +	 *             If a HTTP proxy is being used and the proxy refuses the
 +	 *             connection, then a {@link HTTPProxyException} may be thrown,
 +	 *             which contains the details returned by the proxy. If the
 +	 *             proxy is buggy and does not return a proper HTTP response,
 +	 *             then a normal IOException is thrown instead.
  	 */
  	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
  			throws IOException
 @@ -649,16 +683,17 @@ public class Connection  		tm.setConnectionMonitors(connectionMonitors);
 -		/* Make sure that the runnable below will observe the new value of "tm"
 -		 * and "state" (the runnable will be executed in a different thread, which
 -		 * may be already running, that is why we need a memory barrier here).
 -		 * See also the comment in Channel.java if you
 -		 * are interested in the details.
 +		/*
 +		 * Make sure that the runnable below will observe the new value of "tm"
 +		 * and "state" (the runnable will be executed in a different thread,
 +		 * which may be already running, that is why we need a memory barrier
 +		 * here). See also the comment in Channel.java if you are interested in
 +		 * the details.
  		 * 
 -		 * OKOK, this is paranoid since adding the runnable to the todo list
 -		 * of the TimeoutService will ensure that all writes have been flushed
 -		 * before the Runnable reads anything
 -		 * (there is a synchronized block in TimeoutService.addTimeoutHandler).
 +		 * OKOK, this is paranoid since adding the runnable to the todo list of
 +		 * the TimeoutService will ensure that all writes have been flushed
 +		 * before the Runnable reads anything (there is a synchronized block in
 +		 * TimeoutService.addTimeoutHandler).
  		 */
  		synchronized (tm)
 @@ -719,9 +754,10 @@ public class Connection  				{
  					if (state.timeoutSocketClosed)
  						throw new IOException("This exception will be replaced by the one below =)");
 -					/* Just in case the "cancelTimeoutHandler" invocation came just a little bit
 -					 * too late but the handler did not enter the semaphore yet - we can
 -					 * still stop it.
 +					/*
 +					 * Just in case the "cancelTimeoutHandler" invocation came
 +					 * just a little bit too late but the handler did not enter
 +					 * the semaphore yet - we can still stop it.
  					 */
  					state.isCancelled = true;
  				}
 @@ -740,7 +776,10 @@ public class Connection  			synchronized (state)
  			{
 -				/* Show a clean exception, not something like "the socket is closed!?!" */
 +				/*
 +				 * Show a clean exception, not something like "the socket is
 +				 * closed!?!"
 +				 */
  				if (state.timeoutSocketClosed)
  					throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
  			}
 @@ -755,17 +794,21 @@ public class Connection  	}
  	/**
 -	 * Creates a new {@link LocalPortForwarder}.
 -	 * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
 -	 * port via the secure tunnel to another host (which may or may not be
 -	 * identical to the remote SSH-2 server).
 +	 * Creates a new {@link LocalPortForwarder}. A
 +	 * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
 +	 * at a local port via the secure tunnel to another host (which may or may
 +	 * not be identical to the remote SSH-2 server).
  	 * <p>
 -	 * This method must only be called after one has passed successfully the authentication step.
 -	 * There is no limit on the number of concurrent forwardings.
 +	 * This method must only be called after one has passed successfully the
 +	 * authentication step. There is no limit on the number of concurrent
 +	 * forwardings.
  	 * 
 -	 * @param local_port the local port the LocalPortForwarder shall bind to.
 -	 * @param host_to_connect target address (IP or hostname)
 -	 * @param port_to_connect target port
 +	 * @param local_port
 +	 *            the local port the LocalPortForwarder shall bind to.
 +	 * @param host_to_connect
 +	 *            target address (IP or hostname)
 +	 * @param port_to_connect
 +	 *            target port
  	 * @return A {@link LocalPortForwarder} object.
  	 * @throws IOException
  	 */
 @@ -782,17 +825,22 @@ public class Connection  	}
  	/**
 -	 * Creates a new {@link LocalPortForwarder}.
 -	 * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
 -	 * port via the secure tunnel to another host (which may or may not be
 -	 * identical to the remote SSH-2 server).
 +	 * Creates a new {@link LocalPortForwarder}. A
 +	 * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
 +	 * at a local port via the secure tunnel to another host (which may or may
 +	 * not be identical to the remote SSH-2 server).
  	 * <p>
 -	 * This method must only be called after one has passed successfully the authentication step.
 -	 * There is no limit on the number of concurrent forwardings.
 +	 * This method must only be called after one has passed successfully the
 +	 * authentication step. There is no limit on the number of concurrent
 +	 * forwardings.
  	 * 
 -	 * @param addr specifies the InetSocketAddress where the local socket shall be bound to.
 -	 * @param host_to_connect target address (IP or hostname)
 -	 * @param port_to_connect target port
 +	 * @param addr
 +	 *            specifies the InetSocketAddress where the local socket shall
 +	 *            be bound to.
 +	 * @param host_to_connect
 +	 *            target address (IP or hostname)
 +	 * @param port_to_connect
 +	 *            target port
  	 * @return A {@link LocalPortForwarder} object.
  	 * @throws IOException
  	 */
 @@ -809,10 +857,11 @@ public class Connection  	}
  	/**
 -	 * Creates a new {@link LocalStreamForwarder}.
 -	 * A <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
 -	 * that is being forwarded via the secure tunnel into a TCP/IP connection to another host
 -	 * (which may or may not be identical to the remote SSH-2 server).
 +	 * Creates a new {@link LocalStreamForwarder}. A
 +	 * <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
 +	 * that is being forwarded via the secure tunnel into a TCP/IP connection to
 +	 * another host (which may or may not be identical to the remote SSH-2
 +	 * server).
  	 * 
  	 * @param host_to_connect
  	 * @param port_to_connect
 @@ -832,8 +881,8 @@ public class Connection  	}
  	/**
 -	 * Create a very basic {@link SCPClient} that can be used to copy
 -	 * files from/to the SSH-2 server.
 +	 * Create a very basic {@link SCPClient} that can be used to copy files
 +	 * from/to the SSH-2 server.
  	 * <p>
  	 * Works only after one has passed successfully the authentication step.
  	 * There is no limit on the number of concurrent SCP clients.
 @@ -861,8 +910,8 @@ public class Connection  	 * the only effect that the so far specified parameters will be used for the
  	 * next (server driven) key exchange.
  	 * <p>
 -	 * Note: This implementation will never start a key exchange (other than the initial one)
 -	 * unless you or the SSH-2 server ask for it.
 +	 * Note: This implementation will never start a key exchange (other than the
 +	 * initial one) unless you or the SSH-2 server ask for it.
  	 * 
  	 * @throws IOException
  	 *             In case of any failure behind the scenes.
 @@ -896,9 +945,9 @@ public class Connection  	}
  	/**
 -	 * Returns a {@link ConnectionInfo} object containing the details of
 -	 * the connection. Can be called as soon as the connection has been
 -	 * established (successfully connected).
 +	 * Returns a {@link ConnectionInfo} object containing the details of the
 +	 * connection. Can be called as soon as the connection has been established
 +	 * (successfully connected).
  	 * 
  	 * @return A {@link ConnectionInfo} object.
  	 * @throws IOException
 @@ -921,8 +970,9 @@ public class Connection  	 * Note 1: the username will only be used if no authentication step was done
  	 * so far (it will be used to ask the server for a list of possible
  	 * authentication methods by sending the initial "none" request). Otherwise,
 -	 * this method ignores the user name and returns a cached method list
 -	 * (which is based on the information contained in the last negative server response).
 +	 * this method ignores the user name and returns a cached method list (which
 +	 * is based on the information contained in the last negative server
 +	 * response).
  	 * <p>
  	 * Note 2: the server may return method names that are not supported by this
  	 * implementation.
 @@ -971,9 +1021,9 @@ public class Connection  	/**
  	 * Returns true if there was at least one failed authentication request and
  	 * the last failed authentication request was marked with "partial success"
 -	 * by the server. This is only needed in the rare case of SSH-2 server setups
 -	 * that cannot be satisfied with a single successful authentication request
 -	 * (i.e., multiple authentication steps are needed.)
 +	 * by the server. This is only needed in the rare case of SSH-2 server
 +	 * setups that cannot be satisfied with a single successful authentication
 +	 * request (i.e., multiple authentication steps are needed.)
  	 * <p>
  	 * If you are interested in the details, then have a look at RFC4252.
  	 * 
 @@ -1026,9 +1076,9 @@ public class Connection  	}
  	/**
 -	 * Open a new {@link Session} on this connection. Works only after one has passed
 -	 * successfully the authentication step. There is no limit on the number of
 -	 * concurrent sessions.
 +	 * Open a new {@link Session} on this connection. Works only after one has
 +	 * passed successfully the authentication step. There is no limit on the
 +	 * number of concurrent sessions.
  	 * 
  	 * @return A {@link Session} object.
  	 * @throws IOException
 @@ -1045,8 +1095,9 @@ public class Connection  	}
  	/**
 -	 * Send an SSH_MSG_IGNORE packet. This method will generate a random data attribute
 -	 * (length between 0 (invlusive) and 16 (exclusive) bytes, contents are random bytes).
 +	 * Send an SSH_MSG_IGNORE packet. This method will generate a random data
 +	 * attribute (length between 0 (invlusive) and 16 (exclusive) bytes,
 +	 * contents are random bytes).
  	 * <p>
  	 * This method must only be called once the connection is established.
  	 * 
 @@ -1085,11 +1136,12 @@ public class Connection  	}
  	/**
 -	 * Removes duplicates from a String array, keeps only first occurence
 -	 * of each element. Does not destroy order of elements; can handle nulls.
 -	 * Uses a very efficient O(N^2) algorithm =)
 +	 * Removes duplicates from a String array, keeps only first occurence of
 +	 * each element. Does not destroy order of elements; can handle nulls. Uses
 +	 * a very efficient O(N^2) algorithm =)
  	 * 
 -	 * @param list a String array.
 +	 * @param list
 +	 *            a String array.
  	 * @return a cleaned String array.
  	 */
  	private String[] removeDuplicates(String[] list)
 @@ -1164,7 +1216,8 @@ public class Connection  	 * know what you are doing, you will never need this. Default values are
  	 * defined in the {@link DHGexParameters} class.
  	 * 
 -	 * @param dgp {@link DHGexParameters}, non null.
 +	 * @param dgp
 +	 *            {@link DHGexParameters}, non null.
  	 * 
  	 */
  	public synchronized void setDHGexParameters(DHGexParameters dgp)
 @@ -1205,16 +1258,17 @@ public class Connection  	}
  	/**
 -	 * Define the set of allowed server host key algorithms to be used for
 -	 * the following key exchange operations.
 +	 * Define the set of allowed server host key algorithms to be used for the
 +	 * following key exchange operations.
  	 * <p>
  	 * Unless you know what you are doing, you will never need this.
  	 * 
 -	 * @param algos An array of allowed server host key algorithms.
 -	 * 	SSH-2 defines <code>ssh-dss</code> and <code>ssh-rsa</code>.
 -	 * 	The entries of the array must be ordered after preference, i.e.,
 -	 *  the entry at index 0 is the most preferred one. You must specify
 -	 *  at least one entry.
 +	 * @param algos
 +	 *            An array of allowed server host key algorithms. SSH-2 defines
 +	 *            <code>ssh-dss</code> and <code>ssh-rsa</code>. The
 +	 *            entries of the array must be ordered after preference, i.e.,
 +	 *            the entry at index 0 is the most preferred one. You must
 +	 *            specify at least one entry.
  	 */
  	public synchronized void setServerHostKeyAlgorithms(String[] algos)
  	{
 @@ -1227,13 +1281,16 @@ public class Connection  	}
  	/**
 -	 * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
 +	 * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the
 +	 * underlying socket.
  	 * <p>
  	 * Can be called at any time. If the connection has not yet been established
 -	 * then the passed value will be stored and set after the socket has been set up.
 -	 * The default value that will be used is <code>false</code>.
 +	 * then the passed value will be stored and set after the socket has been
 +	 * set up. The default value that will be used is <code>false</code>.
  	 * 
 -	 * @param enable the argument passed to the <code>Socket.setTCPNoDelay()</code> method.
 +	 * @param enable
 +	 *            the argument passed to the <code>Socket.setTCPNoDelay()</code>
 +	 *            method.
  	 * @throws IOException
  	 */
  	public synchronized void setTCPNoDelay(boolean enable) throws IOException
 @@ -1245,19 +1302,22 @@ public class Connection  	}
  	/**
 -	 * Used to tell the library that the connection shall be established through a proxy server.
 -	 * It only makes sense to call this method before calling the {@link #connect() connect()}
 -	 * method.
 +	 * Used to tell the library that the connection shall be established through
 +	 * a proxy server. It only makes sense to call this method before calling
 +	 * the {@link #connect() connect()} method.
  	 * <p>
  	 * At the moment, only HTTP proxies are supported.
  	 * <p>
 -	 * Note: This method can be called any number of times. The {@link #connect() connect()}
 -	 * method will use the value set in the last preceding invocation of this method.
 +	 * Note: This method can be called any number of times. The
 +	 * {@link #connect() connect()} method will use the value set in the last
 +	 * preceding invocation of this method.
  	 * 
  	 * @see HTTPProxyData
  	 * 
 -	 * @param proxyData Connection information about the proxy. If <code>null</code>, then
 -	 *                  no proxy will be used (non surprisingly, this is also the default).
 +	 * @param proxyData
 +	 *            Connection information about the proxy. If <code>null</code>,
 +	 *            then no proxy will be used (non surprisingly, this is also the
 +	 *            default).
  	 */
  	public synchronized void setProxyData(ProxyData proxyData)
  	{
 @@ -1265,40 +1325,47 @@ public class Connection  	}
  	/**
 -	 * Request a remote port forwarding.
 -	 * If successful, then forwarded connections will be redirected to the given target address.
 -	 * You can cancle a requested remote port forwarding by calling
 +	 * Request a remote port forwarding. If successful, then forwarded
 +	 * connections will be redirected to the given target address. You can
 +	 * cancle a requested remote port forwarding by calling
  	 * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
  	 * <p>
 -	 * A call of this method will block until the peer either agreed or disagreed to your request-
 +	 * A call of this method will block until the peer either agreed or
 +	 * disagreed to your request-
  	 * <p>
  	 * Note 1: this method typically fails if you
  	 * <ul>
 -	 * <li>pass a port number for which the used remote user has not enough permissions (i.e., port
 -	 * < 1024)</li>
 +	 * <li>pass a port number for which the used remote user has not enough
 +	 * permissions (i.e., port < 1024)</li>
  	 * <li>or pass a port number that is already in use on the remote server</li>
  	 * <li>or if remote port forwarding is disabled on the server.</li>
  	 * </ul>
  	 * <p>
 -	 * Note 2: (from the openssh man page): By default, the listening socket on the server will be
 -	 * bound to the loopback interface only. This may be overriden by specifying a bind address.
 -	 * Specifying a remote bind address will only succeed if the server's <b>GatewayPorts</b> option
 -	 * is enabled (see sshd_config(5)).
 +	 * Note 2: (from the openssh man page): By default, the listening socket on
 +	 * the server will be bound to the loopback interface only. This may be
 +	 * overriden by specifying a bind address. Specifying a remote bind address
 +	 * will only succeed if the server's <b>GatewayPorts</b> option is enabled
 +	 * (see sshd_config(5)).
  	 * 
 -	 * @param bindAddress address to bind to on the server:
 -	 *                    <ul>
 -	 *                    <li>"" means that connections are to be accepted on all protocol families
 -	 *                    supported by the SSH implementation</li>
 -	 *                    <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
 -	 *                    <li>"::" means to listen on all IPv6 addresses</li>
 -	 *                    <li>"localhost" means to listen on all protocol families supported by the SSH
 -	 *                    implementation on loopback addresses only, [RFC3330] and RFC3513]</li>
 -	 *                    <li>"127.0.0.1" and "::1" indicate listening on the loopback interfaces for
 -	 *                    IPv4 and IPv6 respectively</li>
 -	 *                    </ul>
 -	 * @param bindPort port number to bind on the server (must be > 0)
 -	 * @param targetAddress the target address (IP or hostname)
 -	 * @param targetPort the target port
 +	 * @param bindAddress
 +	 *            address to bind to on the server:
 +	 *            <ul>
 +	 *            <li>"" means that connections are to be accepted on all
 +	 *            protocol families supported by the SSH implementation</li>
 +	 *            <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
 +	 *            <li>"::" means to listen on all IPv6 addresses</li>
 +	 *            <li>"localhost" means to listen on all protocol families
 +	 *            supported by the SSH implementation on loopback addresses
 +	 *            only, [RFC3330] and RFC3513]</li>
 +	 *            <li>"127.0.0.1" and "::1" indicate listening on the loopback
 +	 *            interfaces for IPv4 and IPv6 respectively</li>
 +	 *            </ul>
 +	 * @param bindPort
 +	 *            port number to bind on the server (must be > 0)
 +	 * @param targetAddress
 +	 *            the target address (IP or hostname)
 +	 * @param targetPort
 +	 *            the target port
  	 * @throws IOException
  	 */
  	public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
 @@ -1317,14 +1384,17 @@ public class Connection  	}
  	/**
 -	 * Cancel an earlier requested remote port forwarding. 
 -	 * Currently active forwardings will not be affected (e.g., disrupted).
 -	 * Note that further connection forwarding requests may be received until
 -	 * this method has returned.
 +	 * Cancel an earlier requested remote port forwarding. Currently active
 +	 * forwardings will not be affected (e.g., disrupted). Note that further
 +	 * connection forwarding requests may be received until this method has
 +	 * returned.
  	 * 
 -	 * @param bindPort the allocated port number on the server
 -	 * @throws IOException if the remote side refuses the cancel request or another low
 -	 *         level error occurs (e.g., the underlying connection is closed)
 +	 * @param bindPort
 +	 *            the allocated port number on the server
 +	 * @throws IOException
 +	 *             if the remote side refuses the cancel request or another low
 +	 *             level error occurs (e.g., the underlying connection is
 +	 *             closed)
  	 */
  	public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
  	{
 @@ -1338,13 +1408,14 @@ public class Connection  	}
  	/**
 -	 * Provide your own instance of SecureRandom. Can be used, e.g., if you
 -	 * want to seed the used SecureRandom generator manually.
 +	 * Provide your own instance of SecureRandom. Can be used, e.g., if you want
 +	 * to seed the used SecureRandom generator manually.
  	 * <p>
 -	 * The SecureRandom instance is used during key exchanges, public key authentication,
 -	 * x11 cookie generation and the like.
 +	 * The SecureRandom instance is used during key exchanges, public key
 +	 * authentication, x11 cookie generation and the like.
  	 * 
 -	 * @param rnd a SecureRandom instance
 +	 * @param rnd
 +	 *            a SecureRandom instance
  	 */
  	public synchronized void setSecureRandom(SecureRandom rnd)
  	{
 @@ -1353,4 +1424,75 @@ public class Connection  		this.generator = rnd;
  	}
 +
 +	/**
 +	 * Enable/disable debug logging. <b>Only do this when requested by Trilead
 +	 * support.</b>
 +	 * <p>
 +	 * For speed reasons, some static variables used to check whether debugging
 +	 * is enabled are not protected with locks. In other words, if you
 +	 * dynamicaly enable/disable debug logging, then some threads may still use
 +	 * the old setting. To be on the safe side, enable debugging before doing
 +	 * the <code>connect()</code> call.
 +	 * 
 +	 * @param enable
 +	 *            on/off
 +	 * @param logger
 +	 *            a {@link DebugLogger DebugLogger} instance, <code>null</code>
 +	 *            means logging using the simple logger which logs all messages
 +	 *            to to stderr. Ignored if enabled is <code>false</code>
 +	 */
 +	public synchronized void enableDebugging(boolean enable, DebugLogger logger)
 +	{
 +		Logger.enabled = enable;
 +
 +		if (enable == false)
 +		{
 +			Logger.logger = null;
 +		}
 +		else
 +		{
 +			if (logger == null)
 +			{
 +				logger = new DebugLogger()
 +				{
 +
 +					public void log(int level, String className, String message)
 +					{
 +						long now = System.currentTimeMillis();
 +						System.err.println(now + " : " + className + ": " + message);
 +					}
 +				};
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * This method can be used to perform end-to-end connection testing. It
 +	 * sends a 'ping' message to the server and waits for the 'pong' from the
 +	 * server.
 +	 * <p>
 +	 * When this method throws an exception, then you can assume that the
 +	 * connection should be abandoned.
 +	 * <p>
 +	 * Note: Works only after one has passed successfully the authentication
 +	 * step.
 +	 * <p>
 +	 * Implementation details: this method sends a SSH_MSG_GLOBAL_REQUEST
 +	 * request ('trilead-ping') to the server and waits for the
 +	 * SSH_MSG_REQUEST_FAILURE reply packet from the server.
 +	 * 
 +	 * @throws IOException
 +	 *             in case of any problem
 +	 */
 +	public synchronized void ping() throws IOException
 +	{
 +		if (tm == null)
 +			throw new IllegalStateException("You need to establish a connection first.");
 +
 +		if (!authenticated)
 +			throw new IllegalStateException("The connection is not authenticated.");
 +
 +		cm.requestGlobalTrileadPing();
 +	}
  }
 diff --git a/lib/src/main/java/com/trilead/ssh2/DebugLogger.java b/lib/src/main/java/com/trilead/ssh2/DebugLogger.java new file mode 100644 index 0000000..731fa28 --- /dev/null +++ b/lib/src/main/java/com/trilead/ssh2/DebugLogger.java @@ -0,0 +1,23 @@ +package com.trilead.ssh2;
 +
 +/**
 + * An interface which needs to be implemented if you
 + * want to capture debugging messages.
 + * 
 + * @see Connection#enableDebugging(boolean, DebugLogger)
 + * 
 + * @author Christian Plattner, plattner@trilead.com
 + * @version $Id: DebugLogger.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
 + */
 +public interface DebugLogger
 +{
 +
 +/**
 + * Log a debug message.
 + * 
 + * @param level 0-99, 99 is a the most verbose level
 + * @param className the class that generated the message
 + * @param message the debug message
 + */
 +	public void log(int level, String className, String message);	
 +}
 diff --git a/lib/src/main/java/com/trilead/ssh2/KnownHosts.java b/lib/src/main/java/com/trilead/ssh2/KnownHosts.java index e89779b..edca0a2 100644 --- a/lib/src/main/java/com/trilead/ssh2/KnownHosts.java +++ b/lib/src/main/java/com/trilead/ssh2/KnownHosts.java @@ -8,6 +8,7 @@ import java.io.File;  import java.io.FileReader;
  import java.io.IOException;
  import java.io.RandomAccessFile;
 +import java.io.UnsupportedEncodingException;
  import java.net.InetAddress;
  import java.net.UnknownHostException;
  import java.security.SecureRandom;
 @@ -39,7 +40,7 @@ import com.trilead.ssh2.signature.RSASHA1Verify;   * <code>KnownHosts</code> for your whole application.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: KnownHosts.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: KnownHosts.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class KnownHosts
 @@ -168,8 +169,16 @@ public class KnownHosts  		HMAC hmac = new HMAC(sha1, salt, salt.length);
 -		hmac.update(hostname.getBytes());
 -
 +		try
 +		{
 +			hmac.update(hostname.getBytes("ISO-8859-1"));
 +		}catch(UnsupportedEncodingException ignore)
 +		{
 +			/* Actually, ISO-8859-1 is supported by all correct
 +			 * Java implementations. But... you never know. */
 +			hmac.update(hostname.getBytes());
 +		}
 +		
  		byte[] dig = new byte[hmac.getDigestLength()];
  		hmac.digest(dig);
 @@ -684,7 +693,7 @@ public class KnownHosts  				raf.write('\n');
  		}
 -		raf.write(new String(entry).getBytes());
 +		raf.write(new String(entry).getBytes("ISO-8859-1"));
  		raf.close();
  	}
 diff --git a/lib/src/main/java/com/trilead/ssh2/SCPClient.java b/lib/src/main/java/com/trilead/ssh2/SCPClient.java index b53040c..8ea248a 100644 --- a/lib/src/main/java/com/trilead/ssh2/SCPClient.java +++ b/lib/src/main/java/com/trilead/ssh2/SCPClient.java @@ -19,7 +19,7 @@ import java.io.OutputStream;   * actually mapping every request to a distinct {@link Session}.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: SCPClient.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: SCPClient.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class SCPClient
 @@ -65,7 +65,10 @@ public class SCPClient  		while (true)
  		{
 -			/* This is a random limit - if your path names are longer, then adjust it */
 +			/*
 +			 * This is a random limit - if your path names are longer, then
 +			 * adjust it
 +			 */
  			if (sb.length() > 8192)
  				throw new IOException("Remote scp sent a too long line");
 @@ -138,7 +141,7 @@ public class SCPClient  		String cline = "C" + mode + " " + data.length + " " + fileName + "\n";
 -		os.write(cline.getBytes());
 +		os.write(cline.getBytes("ISO-8859-1"));
  		os.flush();
  		readResponse(is);
 @@ -149,7 +152,7 @@ public class SCPClient  		readResponse(is);
 -		os.write("E\n".getBytes());
 +		os.write("E\n".getBytes("ISO-8859-1"));
  		os.flush();
  	}
 @@ -176,7 +179,7 @@ public class SCPClient  			String cline = "C" + mode + " " + remain + " " + remoteName + "\n";
 -			os.write(cline.getBytes());
 +			os.write(cline.getBytes("ISO-8859-1"));
  			os.flush();
  			readResponse(is);
 @@ -215,7 +218,7 @@ public class SCPClient  			readResponse(is);
  		}
 -		os.write("E\n".getBytes());
 +		os.write("E\n".getBytes("ISO-8859-1"));
  		os.flush();
  	}
 @@ -379,13 +382,14 @@ public class SCPClient  	}
  	/**
 -	 * Copy a local file to a remote directory, uses mode 0600 when creating
 -	 * the file on the remote side.
 +	 * Copy a local file to a remote directory, uses mode 0600 when creating the
 +	 * file on the remote side.
  	 * 
  	 * @param localFile
  	 *            Path and name of local file.
  	 * @param remoteTargetDirectory
 -	 *            Remote target directory. Use an empty string to specify the default directory.
 +	 *            Remote target directory. Use an empty string to specify the
 +	 *            default directory.
  	 * 
  	 * @throws IOException
  	 */
 @@ -401,7 +405,8 @@ public class SCPClient  	 * @param localFiles
  	 *            Paths and names of local file names.
  	 * @param remoteTargetDirectory
 -	 *            Remote target directory. Use an empty string to specify the default directory.
 +	 *            Remote target directory. Use an empty string to specify the
 +	 *            default directory.
  	 * 
  	 * @throws IOException
  	 */
 @@ -418,7 +423,8 @@ public class SCPClient  	 * @param localFile
  	 *            Path and name of local file.
  	 * @param remoteTargetDirectory
 -	 *            Remote target directory. Use an empty string to specify the default directory.
 +	 *            Remote target directory. Use an empty string to specify the
 +	 *            default directory.
  	 * @param mode
  	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
  	 * @throws IOException
 @@ -429,15 +435,17 @@ public class SCPClient  	}
  	/**
 -	 * Copy a local file to a remote directory, uses the specified mode and remote filename
 -	 * when creating the file on the remote side.
 +	 * Copy a local file to a remote directory, uses the specified mode and
 +	 * remote filename when creating the file on the remote side.
  	 * 
  	 * @param localFile
  	 *            Path and name of local file.
  	 * @param remoteFileName
 -	 *            The name of the file which will be created in the remote target directory.
 +	 *            The name of the file which will be created in the remote
 +	 *            target directory.
  	 * @param remoteTargetDirectory
 -	 *            Remote target directory. Use an empty string to specify the default directory.
 +	 *            Remote target directory. Use an empty string to specify the
 +	 *            default directory.
  	 * @param mode
  	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
  	 * @throws IOException
 @@ -449,15 +457,17 @@ public class SCPClient  	}
  	/**
 -	 * Create a remote file and copy the contents of the passed byte array into it.
 -	 * Uses mode 0600 for creating the remote file.
 +	 * Create a remote file and copy the contents of the passed byte array into
 +	 * it. Uses mode 0600 for creating the remote file.
  	 * 
  	 * @param data
  	 *            the data to be copied into the remote file.
  	 * @param remoteFileName
 -	 *            The name of the file which will be created in the remote target directory.
 +	 *            The name of the file which will be created in the remote
 +	 *            target directory.
  	 * @param remoteTargetDirectory
 -	 *            Remote target directory. Use an empty string to specify the default directory.
 +	 *            Remote target directory. Use an empty string to specify the
 +	 *            default directory.
  	 * @throws IOException
  	 */
 @@ -467,15 +477,18 @@ public class SCPClient  	}
  	/**
 -	 * Create a remote file and copy the contents of the passed byte array into it.
 -	 * The method use the specified mode when creating the file on the remote side.
 +	 * Create a remote file and copy the contents of the passed byte array into
 +	 * it. The method use the specified mode when creating the file on the
 +	 * remote side.
  	 * 
  	 * @param data
  	 *            the data to be copied into the remote file.
  	 * @param remoteFileName
 -	 *            The name of the file which will be created in the remote target directory.
 +	 *            The name of the file which will be created in the remote
 +	 *            target directory.
  	 * @param remoteTargetDirectory
 -	 *            Remote target directory. Use an empty string to specify the default directory.
 +	 *            Remote target directory. Use an empty string to specify the
 +	 *            default directory.
  	 * @param mode
  	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
  	 * @throws IOException
 @@ -523,7 +536,8 @@ public class SCPClient  	 * @param localFiles
  	 *            Paths and names of the local files.
  	 * @param remoteTargetDirectory
 -	 *            Remote target directory. Use an empty string to specify the default directory.
 +	 *            Remote target directory. Use an empty string to specify the
 +	 *            default directory.
  	 * @param mode
  	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
  	 * @throws IOException
 @@ -538,7 +552,10 @@ public class SCPClient  	{
  		Session sess = null;
 -		/* remoteFiles may be null, indicating that the local filenames shall be used */
 +		/*
 +		 * remoteFiles may be null, indicating that the local filenames shall be
 +		 * used
 +		 */
  		if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null))
  			throw new IllegalArgumentException("Null argument.");
 @@ -597,14 +614,15 @@ public class SCPClient  	}
  	/**
 -	 * Download a file from the remote server and pipe its contents into an <code>OutputStream</code>.
 -	 * Please note that, to enable flexible usage of this method, the <code>OutputStream</code> will not
 -	 * be closed nor flushed.  
 +	 * Download a file from the remote server and pipe its contents into an
 +	 * <code>OutputStream</code>. Please note that, to enable flexible usage
 +	 * of this method, the <code>OutputStream</code> will not be closed nor
 +	 * flushed.
  	 * 
  	 * @param remoteFile
 -	 * 			Path and name of the remote file.
 +	 *            Path and name of the remote file.
  	 * @param target
 -	 * 			OutputStream where the contents of the file will be sent to.
 +	 *            OutputStream where the contents of the file will be sent to.
  	 * @throws IOException
  	 */
  	public void get(String remoteFile, OutputStream target) throws IOException
 diff --git a/lib/src/main/java/com/trilead/ssh2/SFTPv3Client.java b/lib/src/main/java/com/trilead/ssh2/SFTPv3Client.java index 364ddf6..be2fa1c 100644 --- a/lib/src/main/java/com/trilead/ssh2/SFTPv3Client.java +++ b/lib/src/main/java/com/trilead/ssh2/SFTPv3Client.java @@ -56,7 +56,7 @@ import com.trilead.ssh2.sftp.Packet;   * {@link #setCharset(String)}.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: SFTPv3Client.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: SFTPv3Client.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
   */
  public class SFTPv3Client
  {
 @@ -319,8 +319,8 @@ public class SFTPv3Client  		{
  			if (debug != null)
  				debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME");
 -			fa.atime = new Integer(tr.readUINT32());
 -			fa.mtime = new Integer(tr.readUINT32());
 +			fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
 +			fa.mtime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
  		}
 @@ -721,8 +721,11 @@ public class SFTPv3Client  			}
  			sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes());
 -
 -			byte[] resp = receiveMessage(34000);
 +		
 +			/* Some servers send here a packet with size > 34000 */
 +			/* To whom it may concern: please learn to read the specs. */
 +			
 +			byte[] resp = receiveMessage(65536);
  			if (debug != null)
  			{
 diff --git a/lib/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java b/lib/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java index 7a44938..56c9c87 100644 --- a/lib/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java +++ b/lib/src/main/java/com/trilead/ssh2/SFTPv3FileAttributes.java @@ -6,7 +6,7 @@ package com.trilead.ssh2;   * about a file on the server. Not all fields may/must be present.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: SFTPv3FileAttributes.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: SFTPv3FileAttributes.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class SFTPv3FileAttributes
 @@ -65,13 +65,13 @@ public class SFTPv3FileAttributes  	 * The ATIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
  	 * <code>NULL</code> if not present.
  	 */
 -	public Integer atime = null;
 +	public Long atime = null;
  	/**
  	 * The MTIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
  	 * <code>NULL</code> if not present.
  	 */
 -	public Integer mtime = null;
 +	public Long mtime = null;
  	/**
  	 * Checks if this entry is a directory.
 diff --git a/lib/src/main/java/com/trilead/ssh2/Session.java b/lib/src/main/java/com/trilead/ssh2/Session.java index 098b3f1..4784537 100644 --- a/lib/src/main/java/com/trilead/ssh2/Session.java +++ b/lib/src/main/java/com/trilead/ssh2/Session.java @@ -18,7 +18,7 @@ import com.trilead.ssh2.channel.X11ServerData;   * a session. However, multiple sessions can be active simultaneously.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: Session.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: Session.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
   */
  public class Session
  {
 @@ -298,6 +298,32 @@ public class Session  		cm.requestSubSystem(cn, name);
  	}
 +	/**
 +	 * This method can be used to perform end-to-end session (i.e., SSH channel)
 +	 * testing. It sends a 'ping' message to the server and waits for the 'pong'
 +	 * from the server.
 +	 * <p>
 +	 * Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request
 +	 * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply
 +	 * packet.
 +	 * 
 +	 * @throws IOException in case of any problem or when the session is closed
 +	 */
 +	public void ping() throws IOException
 +	{
 +		synchronized (this)
 +		{
 +			/*
 +			 * The following is just a nicer error, we would catch it anyway
 +			 * later in the channel code
 +			 */
 +			if (flag_closed)
 +				throw new IOException("This session is closed.");
 +		}
 +
 +		cm.requestChannelTrileadPing(cn);
 +	}
 +	
  	public InputStream getStdout()
  	{
  		return cn.getStdoutStream();
 diff --git a/lib/src/main/java/com/trilead/ssh2/channel/ChannelManager.java b/lib/src/main/java/com/trilead/ssh2/channel/ChannelManager.java index b9282de..ebd7585 100644 --- a/lib/src/main/java/com/trilead/ssh2/channel/ChannelManager.java +++ b/lib/src/main/java/com/trilead/ssh2/channel/ChannelManager.java @@ -9,8 +9,10 @@ import com.trilead.ssh2.ChannelCondition;  import com.trilead.ssh2.log.Logger;
  import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
  import com.trilead.ssh2.packets.PacketChannelOpenFailure;
 +import com.trilead.ssh2.packets.PacketChannelTrileadPing;
  import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
  import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
 +import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
  import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
  import com.trilead.ssh2.packets.PacketOpenSessionChannel;
  import com.trilead.ssh2.packets.PacketSessionExecCommand;
 @@ -23,14 +25,13 @@ import com.trilead.ssh2.packets.TypesReader;  import com.trilead.ssh2.transport.MessageHandler;
  import com.trilead.ssh2.transport.TransportManager;
 -
  /**
   * ChannelManager. Please read the comments in Channel.java.
   * <p>
   * Besides the crypto part, this is the core of the library.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: ChannelManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: ChannelManager.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
   */
  public class ChannelManager implements MessageHandler
  {
 @@ -126,7 +127,7 @@ public class ChannelManager implements MessageHandler  		}
  	}
 -	private final void waitForGlobalSuccessOrFailure() throws IOException
 +	private final boolean waitForGlobalRequestResult() throws IOException
  	{
  		synchronized (channels)
  		{
 @@ -146,20 +147,18 @@ public class ChannelManager implements MessageHandler  				}
  			}
 -			if (globalFailedCounter != 0)
 -			{
 -				throw new IOException("The server denied the request (did you enable port forwarding?)");
 -			}
 +			if ((globalFailedCounter == 0) && (globalSuccessCounter == 1))
 +				return true;
 -			if (globalSuccessCounter == 0)
 -			{
 -				throw new IOException("Illegal state.");
 -			}
 +			if ((globalFailedCounter == 1) && (globalSuccessCounter == 0))
 +				return false;
 +			throw new IOException("Illegal state. The server sent " + globalSuccessCounter
 +					+ " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages.");
  		}
  	}
 -	private final void waitForChannelSuccessOrFailure(Channel c) throws IOException
 +	private final boolean waitForChannelRequestResult(Channel c) throws IOException
  	{
  		synchronized (c)
  		{
 @@ -184,10 +183,14 @@ public class ChannelManager implements MessageHandler  				}
  			}
 -			if (c.failedCounter != 0)
 -			{
 -				throw new IOException("The server denied the request.");
 -			}
 +			if ((c.failedCounter == 0) && (c.successCounter == 1))
 +				return true;
 +
 +			if ((c.failedCounter == 1) && (c.successCounter == 0))
 +				return false;
 +
 +			throw new IOException("Illegal state. The server sent " + c.successCounter
 +					+ " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages.");
  		}
  	}
 @@ -471,7 +474,8 @@ public class ChannelManager implements MessageHandler  		try
  		{
 -			waitForGlobalSuccessOrFailure();
 +			if (waitForGlobalRequestResult() == false)
 +				throw new IOException("The server denied the request (did you enable port forwarding?)");
  		}
  		catch (IOException e)
  		{
 @@ -509,14 +513,20 @@ public class ChannelManager implements MessageHandler  		if (log.isEnabled())
  			log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
 -		waitForGlobalSuccessOrFailure();
 -
 -		/* Only now we are sure that no more forwarded connections will arrive */
 -
 -		synchronized (remoteForwardings)
 +		try
 +		{
 +			if (waitForGlobalRequestResult() == false)
 +				throw new IOException("The server denied the request.");
 +		}
 +		finally
  		{
 -			remoteForwardings.remove(rfd);
 +			synchronized (remoteForwardings)
 +			{
 +				/* Only now we are sure that no more forwarded connections will arrive */
 +				remoteForwardings.remove(rfd);
 +			}
  		}
 +
  	}
  	public void registerThread(IChannelWorkerThread thr) throws IOException
 @@ -571,6 +581,67 @@ public class ChannelManager implements MessageHandler  		return c;
  	}
 +	public void requestGlobalTrileadPing() throws IOException
 +	{
 +		synchronized (channels)
 +		{
 +			globalSuccessCounter = globalFailedCounter = 0;
 +		}
 +
 +		PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing();
 +
 +		tm.sendMessage(pgtp.getPayload());
 +
 +		if (log.isEnabled())
 +			log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'.");
 +
 +		try
 +		{
 +			if (waitForGlobalRequestResult() == true)
 +				throw new IOException("Your server is alive - but buggy. "
 +						+ "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not.");
 +
 +		}
 +		catch (IOException e)
 +		{
 +			throw (IOException) new IOException("The ping request failed.").initCause(e);
 +		}
 +	}
 +
 +	public void requestChannelTrileadPing(Channel c) throws IOException
 +	{
 +		PacketChannelTrileadPing pctp;
 +
 +		synchronized (c)
 +		{
 +			if (c.state != Channel.STATE_OPEN)
 +				throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
 +
 +			pctp = new PacketChannelTrileadPing(c.remoteID);
 +
 +			c.successCounter = c.failedCounter = 0;
 +		}
 +
 +		synchronized (c.channelSendLock)
 +		{
 +			if (c.closeMessageSent)
 +				throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
 +			tm.sendMessage(pctp.getPayload());
 +		}
 +
 +		try
 +		{
 +			if (waitForChannelRequestResult(c) == true)
 +				throw new IOException("Your server is alive - but buggy. "
 +						+ "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not.");
 +
 +		}
 +		catch (IOException e)
 +		{
 +			throw (IOException) new IOException("The ping request failed.").initCause(e);
 +		}
 +	}
 +
  	public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
  			int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
  	{
 @@ -596,7 +667,8 @@ public class ChannelManager implements MessageHandler  		try
  		{
 -			waitForChannelSuccessOrFailure(c);
 +			if (waitForChannelRequestResult(c) == false)
 +				throw new IOException("The server denied the request.");
  		}
  		catch (IOException e)
  		{
 @@ -632,7 +704,8 @@ public class ChannelManager implements MessageHandler  		try
  		{
 -			waitForChannelSuccessOrFailure(c);
 +			if (waitForChannelRequestResult(c) == false)
 +				throw new IOException("The server denied the request.");
  		}
  		catch (IOException e)
  		{
 @@ -663,7 +736,8 @@ public class ChannelManager implements MessageHandler  		try
  		{
 -			waitForChannelSuccessOrFailure(c);
 +			if (waitForChannelRequestResult(c) == false)
 +				throw new IOException("The server denied the request.");
  		}
  		catch (IOException e)
  		{
 @@ -697,7 +771,8 @@ public class ChannelManager implements MessageHandler  		try
  		{
 -			waitForChannelSuccessOrFailure(c);
 +			if (waitForChannelRequestResult(c) == false)
 +				throw new IOException("The server denied the request.");
  		}
  		catch (IOException e)
  		{
 @@ -728,7 +803,8 @@ public class ChannelManager implements MessageHandler  		try
  		{
 -			waitForChannelSuccessOrFailure(c);
 +			if (waitForChannelRequestResult(c) == false)
 +				throw new IOException("The server denied the request.");
  		}
  		catch (IOException e)
  		{
 @@ -1533,11 +1609,11 @@ public class ChannelManager implements MessageHandler  						c.state = Channel.STATE_CLOSED;
  						c.setReasonClosed("The connection is being shutdown");
  						c.closeMessageRecv = true; /*
 -						 * You never know, perhaps
 -						 * we are waiting for a
 -						 * pending close message
 -						 * from the server...
 -						 */
 +																															 * You never know, perhaps
 +																															 * we are waiting for a
 +																															 * pending close message
 +																															 * from the server...
 +																															 */
  						c.notifyAll();
  					}
  				}
 diff --git a/lib/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java b/lib/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java index d4417ff..8ee05a2 100644 --- a/lib/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java +++ b/lib/src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java @@ -13,7 +13,7 @@ import com.trilead.ssh2.log.Logger;   * RemoteX11AcceptThread.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: RemoteX11AcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: RemoteX11AcceptThread.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class RemoteX11AcceptThread extends Thread
  {
 @@ -123,7 +123,7 @@ public class RemoteX11AcceptThread extends Thread  			if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
  				throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");
 -			if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName)) == false)
 +			if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName, "ISO-8859-1")) == false)
  				throw new IOException("Unknown X11 authorization protocol!");
  			if (authProtocolDataLength != 16)
 diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java b/lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java index c6f1a4e..ac1b842 100644 --- a/lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java +++ b/lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java @@ -15,12 +15,11 @@ import com.trilead.ssh2.crypto.digest.MD5;  import com.trilead.ssh2.signature.DSAPrivateKey;
  import com.trilead.ssh2.signature.RSAPrivateKey;
 -
  /**
   * PEM Support.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: PEMDecoder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class PEMDecoder
  {
 @@ -82,7 +81,8 @@ public class PEMDecoder  		while (true)
  		{
  			md5.update(password, 0, password.length);
 -			md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the salt in this step.
 +			md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the
 +			// salt in this step.
  			// This took me two hours until I got AES-xxx running.
  			int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
 @@ -317,7 +317,7 @@ public class PEMDecoder  			if (password == null)
  				throw new IOException("PEM is encrypted, but no password was specified");
 -			decryptPEM(ps, password.getBytes());
 +			decryptPEM(ps, password.getBytes("ISO-8859-1"));
  		}
  		if (ps.pemType == PEM_DSA_PRIVATE_KEY)
 diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java b/lib/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java index 5cc37ac..cf18f43 100644 --- a/lib/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java +++ b/lib/src/main/java/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java @@ -7,7 +7,7 @@ import java.util.Vector;   * BlockCipherFactory.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: BlockCipherFactory.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
 + * @version $Id: BlockCipherFactory.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class BlockCipherFactory
  {
 @@ -36,7 +36,7 @@ public class BlockCipherFactory  		ciphers.addElement(new CipherEntry("aes256-ctr", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
  		ciphers.addElement(new CipherEntry("aes192-ctr", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
  		ciphers.addElement(new CipherEntry("aes128-ctr", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
 -		ciphers.addElement(new CipherEntry("blowfish-ctr", 8, 16, "com.trileadssh2.crypto.cipher.BlowFish"));
 +		ciphers.addElement(new CipherEntry("blowfish-ctr", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish"));
  		ciphers.addElement(new CipherEntry("aes256-cbc", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
  		ciphers.addElement(new CipherEntry("aes192-cbc", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
 diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java b/lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java index 85a7696..5622a72 100644 --- a/lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java +++ b/lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java @@ -1,6 +1,7 @@  package com.trilead.ssh2.crypto.dh;
 +import java.io.UnsupportedEncodingException;
  import java.math.BigInteger;
  import java.security.SecureRandom;
 @@ -12,7 +13,7 @@ import com.trilead.ssh2.log.Logger;   * DhExchange.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: DhExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 + * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class DhExchange
  {
 @@ -122,14 +123,14 @@ public class DhExchange  	}
  	public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
 -			byte[] serverKexPayload, byte[] hostKey)
 +			byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
  	{
  		HashForSSH2Types hash = new HashForSSH2Types("SHA1");
  		if (log.isEnabled())
  		{
 -			log.log(90, "Client: '" + new String(clientversion) + "'");
 -			log.log(90, "Server: '" + new String(serverversion) + "'");
 +			log.log(90, "Client: '" + new String(clientversion, "ISO-8859-1") + "'");
 +			log.log(90, "Server: '" + new String(serverversion, "ISO-8859-1") + "'");
  		}
  		hash.updateByteString(clientversion);
 diff --git a/lib/src/main/java/com/trilead/ssh2/log/Logger.java b/lib/src/main/java/com/trilead/ssh2/log/Logger.java index fe1a944..fe388f7 100644 --- a/lib/src/main/java/com/trilead/ssh2/log/Logger.java +++ b/lib/src/main/java/com/trilead/ssh2/log/Logger.java @@ -1,21 +1,27 @@  package com.trilead.ssh2.log;
 +import com.trilead.ssh2.DebugLogger;
 +
  /**
   * Logger - a very simple logger, mainly used during development.
   * Is not based on log4j (to reduce external dependencies).
   * However, if needed, something like log4j could easily be
   * hooked in.
 + * <p>
 + * For speed reasons, the static variables are not protected
 + * with semaphores. In other words, if you dynamicaly change the
 + * logging settings, then some threads may still use the old setting.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: Logger.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: Logger.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
   */
  public class Logger
  {
 -	private static final boolean enabled = false;
 -	private static final int logLevel = 99;
 -
 +	public static boolean enabled = false;
 +	public static DebugLogger logger = null;
 +	
  	private String className;
  	public final static Logger getLogger(Class x)
 @@ -35,15 +41,14 @@ public class Logger  	public final void log(int level, String message)
  	{
 -		if ((enabled) && (level <= logLevel))
 -		{
 -			long now = System.currentTimeMillis();
 -
 -			synchronized (this)
 -			{
 -				System.err.println(now + " : " + className + ": " + message);
 -				// or send it to log4j or whatever...
 -			}
 -		}
 +		if (!enabled)
 +			return;
 +		
 +		DebugLogger target = logger;
 +		
 +		if (target == null)
 +			return;
 +		
 +		target.log(level, className, message);
  	}
  }
 diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java new file mode 100644 index 0000000..18002aa --- /dev/null +++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java @@ -0,0 +1,35 @@ +
 +package com.trilead.ssh2.packets;
 +
 +/**
 + * PacketChannelTrileadPing.
 + * 
 + * @author Christian Plattner, plattner@trilead.com
 + * @version $Id: PacketChannelTrileadPing.java,v 1.1 2008/03/03 07:01:36
 + *          cplattne Exp $
 + */
 +public class PacketChannelTrileadPing
 +{
 +	byte[] payload;
 +
 +	public int recipientChannelID;
 +
 +	public PacketChannelTrileadPing(int recipientChannelID)
 +	{
 +		this.recipientChannelID = recipientChannelID;
 +	}
 +
 +	public byte[] getPayload()
 +	{
 +		if (payload == null)
 +		{
 +			TypesWriter tw = new TypesWriter();
 +			tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
 +			tw.writeUINT32(recipientChannelID);
 +			tw.writeString("trilead-ping");
 +			tw.writeBoolean(true);
 +			payload = tw.getBytes();
 +		}
 +		return payload;
 +	}
 +}
 diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java index b88bf11..1811fb3 100644 --- a/lib/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java +++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketDisconnect.java @@ -1,11 +1,13 @@ +
  package com.trilead.ssh2.packets;
 +
  import java.io.IOException;
  /**
   * PacketDisconnect.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: PacketDisconnect.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
 + * @version $Id: PacketDisconnect.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class PacketDisconnect
  {
 @@ -25,8 +27,7 @@ public class PacketDisconnect  		int packet_type = tr.readByte();
  		if (packet_type != Packets.SSH_MSG_DISCONNECT)
 -			throw new IOException("This is not a Disconnect Packet! ("
 -					+ packet_type + ")");
 +			throw new IOException("This is not a Disconnect Packet! (" + packet_type + ")");
  		reason = tr.readUINT32();
  		desc = tr.readString();
 @@ -39,7 +40,7 @@ public class PacketDisconnect  		this.desc = desc;
  		this.lang = lang;
  	}
 -	
 +
  	public byte[] getPayload()
  	{
  		if (payload == null)
 diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java index e102322..bc54b2e 100644 --- a/lib/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java +++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java @@ -5,7 +5,8 @@ package com.trilead.ssh2.packets;   * PacketGlobalCancelForwardRequest.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: PacketGlobalCancelForwardRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
 + * @version $Id: PacketGlobalCancelForwardRequest.java,v 1.1 2007/10/15 12:49:55
 + *          cplattne Exp $
   */
  public class PacketGlobalCancelForwardRequest
  {
 @@ -28,7 +29,7 @@ public class PacketGlobalCancelForwardRequest  		{
  			TypesWriter tw = new TypesWriter();
  			tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
 -			
 +
  			tw.writeString("cancel-tcpip-forward");
  			tw.writeBoolean(wantReply);
  			tw.writeString(bindAddress);
 diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java new file mode 100644 index 0000000..5b9013d --- /dev/null +++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java @@ -0,0 +1,32 @@ +
 +package com.trilead.ssh2.packets;
 +
 +/**
 + * PacketGlobalTrileadPing.
 + * 
 + * @author Christian Plattner, plattner@trilead.com
 + * @version $Id: PacketGlobalTrileadPing.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
 + */
 +public class PacketGlobalTrileadPing
 +{
 +	byte[] payload;
 +
 +	public PacketGlobalTrileadPing()
 +	{
 +	}
 +
 +	public byte[] getPayload()
 +	{
 +		if (payload == null)
 +		{
 +			TypesWriter tw = new TypesWriter();
 +			tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
 +			
 +			tw.writeString("trilead-ping");
 +			tw.writeBoolean(true);
 +
 +			payload = tw.getBytes();
 +		}
 +		return payload;
 +	}
 +}
 diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java index f0b3c70..5081651 100644 --- a/lib/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java +++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketServiceAccept.java @@ -1,3 +1,4 @@ +
  package com.trilead.ssh2.packets;
  import java.io.IOException;
 @@ -6,14 +7,14 @@ import java.io.IOException;   * PacketServiceAccept.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: PacketServiceAccept.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
 + * @version $Id: PacketServiceAccept.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class PacketServiceAccept
  {
  	byte[] payload;
  	String serviceName;
 -	
 +
  	public PacketServiceAccept(String serviceName)
  	{
  		this.serviceName = serviceName;
 @@ -29,11 +30,19 @@ public class PacketServiceAccept  		int packet_type = tr.readByte();
  		if (packet_type != Packets.SSH_MSG_SERVICE_ACCEPT)
 -			throw new IOException("This is not a SSH_MSG_SERVICE_ACCEPT! ("
 -					+ packet_type + ")");
 +			throw new IOException("This is not a SSH_MSG_SERVICE_ACCEPT! (" + packet_type + ")");
 +
 +		/* Be clever in case the server is not. Some servers seem to violate RFC4253 */
 +
 +		if (tr.remain() > 0)
 +		{
 +			serviceName = tr.readString();
 +		}
 +		else
 +		{
 +			serviceName = "";
 +		}
 -		serviceName = tr.readString();
 -		
  		if (tr.remain() != 0)
  			throw new IOException("Padding in SSH_MSG_SERVICE_ACCEPT packet!");
  	}
 diff --git a/lib/src/main/java/com/trilead/ssh2/packets/TypesReader.java b/lib/src/main/java/com/trilead/ssh2/packets/TypesReader.java index 2b770cb..c8d73e7 100644 --- a/lib/src/main/java/com/trilead/ssh2/packets/TypesReader.java +++ b/lib/src/main/java/com/trilead/ssh2/packets/TypesReader.java @@ -11,7 +11,7 @@ import com.trilead.ssh2.util.Tokenizer;   * TypesReader.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: TypesReader.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
 + * @version $Id: TypesReader.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class TypesReader
  {
 @@ -157,7 +157,8 @@ public class TypesReader  		if ((len + pos) > max)
  			throw new IOException("Malformed SSH string.");
 -		String res = new String(arr, pos, len);
 +		String res = new String(arr, pos, len, "ISO-8859-1");
 +		
  		pos += len;
  		return res;
 diff --git a/lib/src/main/java/com/trilead/ssh2/packets/TypesWriter.java b/lib/src/main/java/com/trilead/ssh2/packets/TypesWriter.java index 41fe7b5..f2e5ef3 100644 --- a/lib/src/main/java/com/trilead/ssh2/packets/TypesWriter.java +++ b/lib/src/main/java/com/trilead/ssh2/packets/TypesWriter.java @@ -8,7 +8,7 @@ import java.math.BigInteger;   * TypesWriter.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: TypesWriter.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
 + * @version $Id: TypesWriter.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class TypesWriter
  {
 @@ -39,7 +39,7 @@ public class TypesWriter  		System.arraycopy(arr, 0, dst, 0, pos);
  		return dst;
  	}
 -	
 +
  	public void getBytes(byte dst[])
  	{
  		System.arraycopy(arr, 0, dst, 0, pos);
 @@ -113,7 +113,7 @@ public class TypesWriter  	{
  		writeBytes(buff, 0, buff.length);
  	}
 -	
 +
  	public void writeBytes(byte[] buff, int off, int len)
  	{
  		if ((pos + len) > arr.length)
 @@ -131,16 +131,26 @@ public class TypesWriter  	public void writeString(String v)
  	{
 -		byte[] b = v.getBytes();
 +		byte[] b;
 +
 +		try
 +		{
 +			/* All Java JVMs must support ISO-8859-1 */
 +			b = v.getBytes("ISO-8859-1");
 +		}
 +		catch (UnsupportedEncodingException ignore)
 +		{
 +			b = v.getBytes();
 +		}
  		writeUINT32(b.length);
  		writeBytes(b, 0, b.length);
  	}
 -	
 +
  	public void writeString(String v, String charsetName) throws UnsupportedEncodingException
  	{
  		byte[] b = (charsetName == null) ? v.getBytes() : v.getBytes(charsetName);
 -		
 +
  		writeUINT32(b.length);
  		writeBytes(b, 0, b.length);
  	}
 diff --git a/lib/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java b/lib/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java index 04c4b20..c838ebd 100644 --- a/lib/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java +++ b/lib/src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java @@ -15,7 +15,7 @@ import com.trilead.ssh2.packets.TypesWriter;   * DSASHA1Verify.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: DSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 + * @version $Id: DSASHA1Verify.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class DSASHA1Verify
  {
 @@ -80,20 +80,31 @@ public class DSASHA1Verify  	public static DSASignature decodeSSHDSASignature(byte[] sig) throws IOException
  	{
 -		TypesReader tr = new TypesReader(sig);
 +		byte[] rsArray = null;
 +		
 +		if (sig.length == 40)
 +		{
 +			/* OK, another broken SSH server. */
 +			rsArray = sig;	
 +		}
 +		else
 +		{
 +			/* Hopefully a server obeing the standard... */
 +			TypesReader tr = new TypesReader(sig);
 -		String sig_format = tr.readString();
 +			String sig_format = tr.readString();
 -		if (sig_format.equals("ssh-dss") == false)
 -			throw new IOException("Peer sent wrong signature format");
 +			if (sig_format.equals("ssh-dss") == false)
 +				throw new IOException("Peer sent wrong signature format");
 -		byte[] rsArray = tr.readByteString();
 +			rsArray = tr.readByteString();
 -		if (rsArray.length != 40)
 -			throw new IOException("Peer sent corrupt signature");
 +			if (rsArray.length != 40)
 +				throw new IOException("Peer sent corrupt signature");
 -		if (tr.remain() != 0)
 -			throw new IOException("Padding in DSA signature!");
 +			if (tr.remain() != 0)
 +				throw new IOException("Padding in DSA signature!");
 +		}
  		/* Remember, s and r are unsigned ints. */
 diff --git a/lib/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java b/lib/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java index 6fa4a00..23726f3 100644 --- a/lib/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java +++ b/lib/src/main/java/com/trilead/ssh2/transport/ClientServerHello.java @@ -4,15 +4,15 @@ package com.trilead.ssh2.transport;  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
 +import java.io.UnsupportedEncodingException;
  import com.trilead.ssh2.Connection;
 -
  /**
   * ClientServerHello.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: ClientServerHello.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 + * @version $Id: ClientServerHello.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class ClientServerHello
  {
 @@ -58,7 +58,7 @@ public class ClientServerHello  	{
  		client_line = "SSH-2.0-" + Connection.identification;
 -		bo.write((client_line + "\r\n").getBytes());
 +		bo.write((client_line + "\r\n").getBytes("ISO-8859-1"));
  		bo.flush();
  		byte[] serverVersion = new byte[512];
 @@ -67,7 +67,7 @@ public class ClientServerHello  		{
  			int len = readLineRN(bi, serverVersion);
 -			server_line = new String(serverVersion, 0, len);
 +			server_line = new String(serverVersion, 0, len, "ISO-8859-1");
  			if (server_line.startsWith("SSH-"))
  				break;
 @@ -90,7 +90,18 @@ public class ClientServerHello  	 */
  	public byte[] getClientString()
  	{
 -		return client_line.getBytes();
 +		byte[] result;
 +
 +		try
 +		{
 +			result = client_line.getBytes("ISO-8859-1");
 +		}
 +		catch (UnsupportedEncodingException ign)
 +		{
 +			result = client_line.getBytes();
 +		}
 +
 +		return result;
  	}
  	/**
 @@ -98,6 +109,17 @@ public class ClientServerHello  	 */
  	public byte[] getServerString()
  	{
 -		return server_line.getBytes();
 +		byte[] result;
 +
 +		try
 +		{
 +			result = server_line.getBytes("ISO-8859-1");
 +		}
 +		catch (UnsupportedEncodingException ign)
 +		{
 +			result = server_line.getBytes();
 +		}
 +
 +		return result;
  	}
  }
 diff --git a/lib/src/main/java/com/trilead/ssh2/transport/TransportManager.java b/lib/src/main/java/com/trilead/ssh2/transport/TransportManager.java index f577c2b..aeb4fce 100644 --- a/lib/src/main/java/com/trilead/ssh2/transport/TransportManager.java +++ b/lib/src/main/java/com/trilead/ssh2/transport/TransportManager.java @@ -49,7 +49,7 @@ import com.trilead.ssh2.util.Tokenizer;   * TransportManager.
   * 
   * @author Christian Plattner, plattner@trilead.com
 - * @version $Id: TransportManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 + * @version $Id: TransportManager.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
   */
  public class TransportManager
  {
 @@ -366,7 +366,7 @@ public class TransportManager  			if ((pd.proxyUser != null) && (pd.proxyPass != null))
  			{
  				String credentials = pd.proxyUser + ":" + pd.proxyPass;
 -				char[] encoded = Base64.encode(credentials.getBytes());
 +				char[] encoded = Base64.encode(credentials.getBytes("ISO-8859-1"));
  				sb.append("Proxy-Authorization: Basic ");
  				sb.append(encoded);
  				sb.append("\r\n");
 @@ -388,7 +388,7 @@ public class TransportManager  			OutputStream out = sock.getOutputStream();
 -			out.write(sb.toString().getBytes());
 +			out.write(sb.toString().getBytes("ISO-8859-1"));
  			out.flush();
  			/* Now parse the HTTP response */
 @@ -398,7 +398,7 @@ public class TransportManager  			int len = ClientServerHello.readLineRN(in, buffer);
 -			String httpReponse = new String(buffer, 0, len);
 +			String httpReponse = new String(buffer, 0, len, "ISO-8859-1");
  			if (httpReponse.startsWith("HTTP/") == false)
  				throw new IOException("The proxy did not send back a valid HTTP response.");
  | 
