From c36f336e6f294313cdab84352b108beea4607e48 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 26 Aug 2008 05:10:04 +0000 Subject: Update Trilead SSH-2 for Java from build 211 to build 213 --- lib/src/main/java/com/trilead/ssh2/Connection.java | 624 +++++++++++++-------- .../main/java/com/trilead/ssh2/DebugLogger.java | 23 + lib/src/main/java/com/trilead/ssh2/KnownHosts.java | 17 +- lib/src/main/java/com/trilead/ssh2/SCPClient.java | 78 ++- .../main/java/com/trilead/ssh2/SFTPv3Client.java | 13 +- .../com/trilead/ssh2/SFTPv3FileAttributes.java | 6 +- lib/src/main/java/com/trilead/ssh2/Session.java | 28 +- .../com/trilead/ssh2/channel/ChannelManager.java | 142 +++-- .../ssh2/channel/RemoteX11AcceptThread.java | 4 +- .../java/com/trilead/ssh2/crypto/PEMDecoder.java | 8 +- .../ssh2/crypto/cipher/BlockCipherFactory.java | 4 +- .../com/trilead/ssh2/crypto/dh/DhExchange.java | 9 +- lib/src/main/java/com/trilead/ssh2/log/Logger.java | 33 +- .../ssh2/packets/PacketChannelTrileadPing.java | 35 ++ .../com/trilead/ssh2/packets/PacketDisconnect.java | 9 +- .../packets/PacketGlobalCancelForwardRequest.java | 5 +- .../ssh2/packets/PacketGlobalTrileadPing.java | 32 ++ .../trilead/ssh2/packets/PacketServiceAccept.java | 21 +- .../java/com/trilead/ssh2/packets/TypesReader.java | 5 +- .../java/com/trilead/ssh2/packets/TypesWriter.java | 22 +- .../com/trilead/ssh2/signature/DSASHA1Verify.java | 31 +- .../trilead/ssh2/transport/ClientServerHello.java | 34 +- .../trilead/ssh2/transport/TransportManager.java | 8 +- 23 files changed, 808 insertions(+), 383 deletions(-) create mode 100644 lib/src/main/java/com/trilead/ssh2/DebugLogger.java create mode 100644 lib/src/main/java/com/trilead/ssh2/packets/PacketChannelTrileadPing.java create mode 100644 lib/src/main/java/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java (limited to 'lib/src') 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; *
    *
  1. creates a {@link #Connection(String) Connection} object.
  2. *
  3. calls the {@link #connect() connect()} method.
  4. - *
  5. calls some of the authentication methods (e.g., {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).
  6. - *
  7. calls one or several times the {@link #openSession() openSession()} method.
  8. - *
  9. finally, one must close the connection and release resources with the {@link #close() close()} method.
  10. + *
  11. calls some of the authentication methods (e.g., + * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).
  12. + *
  13. calls one or several times the {@link #openSession() openSession()} + * method.
  14. + *
  15. finally, one must close the connection and release resources with the + * {@link #close() close()} method.
  16. *
* * @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 Connection object which can then be used * to establish a connection to the specified SSH-2 server. *

- * 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 null submethod list. + * A wrapper that calls + * {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback) + * authenticateWithKeyboardInteractivewith} a null submethod + * list. * * @param user * A String holding the username. @@ -276,12 +285,14 @@ public class Connection * the remaining possible methods). *

* 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()}. *

- * 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). *

* 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)}. *

- * 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. *

* If the authentication phase is complete, true will be * returned. If further authentication steps are needed, false - * is returned and one can retry by any other authentication method - * (use the getRemainingAuthMethods method to get a list of - * the remaining possible methods). + * is returned and one can retry by any other authentication method (use the + * getRemainingAuthMethods 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 =). *

- * 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. *

* If the authentication phase is complete, true will be * returned. If the server does not accept the request (or if further @@ -385,23 +397,24 @@ public class Connection * (use the getRemainingAuthMethods method to get a list of * the remaining possible methods). *

- * 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 String holding the username. * @param pemPrivateKey - * A char[] 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 char[] 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 null. + * 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 null. * * @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 authenticateWithPublicKey(String, char[], String). + * A convenience wrapper function which reads in a private key (PEM format, + * either DSA or RSA) and then calls + * authenticateWithPublicKey(String, char[], String). *

- * 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 String holding the username. * @param pemFile - * A File 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 File 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 null. + * If the PEM file is encrypted then you must specify the + * password. Otherwise, this argument will be ignored and can be + * set to null. * * @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 - * connect() 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 + * connect() 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.) *

* You can add as many monitors as you like. * * @see ConnectionMonitor * - * @param cmon An object implementing the ConnectionMonitor interface. + * @param cmon + * An object implementing the ConnectionMonitor + * 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 verifier to ask for permission to proceed. - * If verifier is null, 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 + * verifier to ask for permission to proceed. If + * verifier is null, 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). *

* 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). *

- * 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). *

- * 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). *

- * 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). *

- * 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 connect() 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 + * connect() again. * * @param verifier - * An object that implements the - * {@link ServerHostKeyVerifier} interface. Pass null - * to accept any server host key - NOT recommended. - * + * An object that implements the {@link ServerHostKeyVerifier} + * interface. Pass null 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 - * verifier callback, but it will only have an effect after - * the verifier 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 verifier callback, + * but it will only have an effect after the + * verifier 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 verifier or there is problem during - * the initial crypto setup (e.g., the signature sent by the server is wrong). - *

- * In case of a timeout (either connectTimeout or kexTimeout) - * a SocketTimeoutException is thrown. - *

- * 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 - * connect() again without having called {@link #close()} first. - *

- * 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 verifier or there is problem + * during the initial crypto setup (e.g., the signature sent by + * the server is wrong). + *

+ * In case of a timeout (either connectTimeout or kexTimeout) a + * SocketTimeoutException is thrown. + *

+ * 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 connect() again + * without having called {@link #close()} first. + *

+ * 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 LocalPortForwarder 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 + * LocalPortForwarder 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). *

- * 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 LocalPortForwarder 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 + * LocalPortForwarder 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). *

- * 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 LocalStreamForwarder 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 + * LocalStreamForwarder 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. *

* 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. *

- * 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). *

* 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.) *

* 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). *

* 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. *

* 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 ssh-dss and ssh-rsa. - * 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 + * ssh-dss and ssh-rsa. 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. *

* 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 false. + * then the passed value will be stored and set after the socket has been + * set up. The default value that will be used is false. * - * @param enable the argument passed to the Socket.setTCPNoDelay() method. + * @param enable + * the argument passed to the Socket.setTCPNoDelay() + * 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. *

* At the moment, only HTTP proxies are supported. *

- * 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 null, then - * no proxy will be used (non surprisingly, this is also the default). + * @param proxyData + * Connection information about the proxy. If null, + * 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()}. *

- * 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- *

* Note 1: this method typically fails if you *

*

- * 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 GatewayPorts 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 GatewayPorts option is enabled + * (see sshd_config(5)). * - * @param bindAddress address to bind to on the server: - *

- * @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: + * + * @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. *

- * 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. Only do this when requested by Trilead + * support. + *

+ * 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 connect() call. + * + * @param enable + * on/off + * @param logger + * a {@link DebugLogger DebugLogger} instance, null + * means logging using the simple logger which logs all messages + * to to stderr. Ignored if enabled is false + */ + 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. + *

+ * When this method throws an exception, then you can assume that the + * connection should be abandoned. + *

+ * Note: Works only after one has passed successfully the authentication + * step. + *

+ * 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; * KnownHosts 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 OutputStream. - * Please note that, to enable flexible usage of this method, the OutputStream will not - * be closed nor flushed. + * Download a file from the remote server and pipe its contents into an + * OutputStream. Please note that, to enable flexible usage + * of this method, the OutputStream 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. * NULL if not present. */ - public Integer atime = null; + public Long atime = null; /** * The MTIME attribute. Represented as seconds from Jan 1, 1970 in UTC. * NULL 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. + *

+ * 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. *

* 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. + *

+ * 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."); -- cgit v1.2.3