diff options
Diffstat (limited to 'lib/src')
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.");
|