2 package com.trilead.ssh2;
\r
4 import java.io.CharArrayWriter;
\r
6 import java.io.FileReader;
\r
7 import java.io.IOException;
\r
8 import java.net.InetSocketAddress;
\r
9 import java.net.SocketTimeoutException;
\r
10 import java.security.SecureRandom;
\r
11 import java.util.Vector;
\r
13 import com.trilead.ssh2.auth.AuthenticationManager;
\r
14 import com.trilead.ssh2.channel.ChannelManager;
\r
15 import com.trilead.ssh2.crypto.CryptoWishList;
\r
16 import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
\r
17 import com.trilead.ssh2.crypto.digest.MAC;
\r
18 import com.trilead.ssh2.log.Logger;
\r
19 import com.trilead.ssh2.packets.PacketIgnore;
\r
20 import com.trilead.ssh2.transport.KexManager;
\r
21 import com.trilead.ssh2.transport.TransportManager;
\r
22 import com.trilead.ssh2.util.TimeoutService;
\r
23 import com.trilead.ssh2.util.TimeoutService.TimeoutToken;
\r
26 * A <code>Connection</code> is used to establish an encrypted TCP/IP
\r
27 * connection to a SSH-2 server.
\r
31 * <li>creates a {@link #Connection(String) Connection} object.</li>
\r
32 * <li>calls the {@link #connect() connect()} method.</li>
\r
33 * <li>calls some of the authentication methods (e.g.,
\r
34 * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
\r
35 * <li>calls one or several times the {@link #openSession() openSession()}
\r
37 * <li>finally, one must close the connection and release resources with the
\r
38 * {@link #close() close()} method.</li>
\r
41 * @author Christian Plattner, plattner@trilead.com
\r
42 * @version $Id: Connection.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
\r
45 public class Connection
\r
48 * The identifier presented to the SSH-2 server.
\r
50 public final static String identification = "TrileadSSH2Java_213";
\r
53 * Will be used to generate all random data needed for the current
\r
54 * connection. Note: SecureRandom.nextBytes() is thread safe.
\r
56 private SecureRandom generator;
\r
59 * Unless you know what you are doing, you will never need this.
\r
61 * @return The list of supported cipher algorithms by this implementation.
\r
63 public static synchronized String[] getAvailableCiphers()
\r
65 return BlockCipherFactory.getDefaultCipherList();
\r
69 * Unless you know what you are doing, you will never need this.
\r
71 * @return The list of supported MAC algorthims by this implementation.
\r
73 public static synchronized String[] getAvailableMACs()
\r
75 return MAC.getMacList();
\r
79 * Unless you know what you are doing, you will never need this.
\r
81 * @return The list of supported server host key algorthims by this
\r
84 public static synchronized String[] getAvailableServerHostKeyAlgorithms()
\r
86 return KexManager.getDefaultServerHostkeyAlgorithmList();
\r
89 private AuthenticationManager am;
\r
91 private boolean authenticated = false;
\r
92 private boolean compression = false;
\r
93 private ChannelManager cm;
\r
95 private CryptoWishList cryptoWishList = new CryptoWishList();
\r
97 private DHGexParameters dhgexpara = new DHGexParameters();
\r
99 private final String hostname;
\r
101 private final int port;
\r
103 private TransportManager tm;
\r
105 private boolean tcpNoDelay = false;
\r
107 private ProxyData proxyData = null;
\r
109 private Vector<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
\r
112 * Prepares a fresh <code>Connection</code> object which can then be used
\r
113 * to establish a connection to the specified SSH-2 server.
\r
115 * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
\r
118 * the hostname of the SSH-2 server.
\r
120 public Connection(String hostname)
\r
122 this(hostname, 22);
\r
126 * Prepares a fresh <code>Connection</code> object which can then be used
\r
127 * to establish a connection to the specified SSH-2 server.
\r
130 * the host where we later want to connect to.
\r
132 * port on the server, normally 22.
\r
134 public Connection(String hostname, int port)
\r
136 this.hostname = hostname;
\r
141 * After a successful connect, one has to authenticate oneself. This method
\r
142 * is based on DSA (it uses DSA to sign a challenge sent by the server).
\r
144 * If the authentication phase is complete, <code>true</code> will be
\r
145 * returned. If the server does not accept the request (or if further
\r
146 * authentication steps are needed), <code>false</code> is returned and
\r
147 * one can retry either by using this or any other authentication method
\r
148 * (use the <code>getRemainingAuthMethods</code> method to get a list of
\r
149 * the remaining possible methods).
\r
152 * A <code>String</code> holding the username.
\r
154 * A <code>String</code> containing the DSA private key of the
\r
155 * user in OpenSSH key format (PEM, you can't miss the
\r
156 * "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
\r
159 * If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
\r
160 * must specify the password. Otherwise, this argument will be
\r
161 * ignored and can be set to <code>null</code>.
\r
163 * @return whether the connection is now authenticated.
\r
164 * @throws IOException
\r
166 * @deprecated You should use one of the
\r
167 * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
\r
168 * methods, this method is just a wrapper for it and will
\r
169 * disappear in future builds.
\r
172 public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
\r
175 throw new IllegalStateException("Connection is not established!");
\r
178 throw new IllegalStateException("Connection is already authenticated!");
\r
181 am = new AuthenticationManager(tm);
\r
184 cm = new ChannelManager(tm);
\r
187 throw new IllegalArgumentException("user argument is null");
\r
190 throw new IllegalArgumentException("pem argument is null");
\r
192 authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
\r
194 return authenticated;
\r
198 * A wrapper that calls
\r
199 * {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
\r
200 * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod
\r
204 * A <code>String</code> holding the username.
\r
206 * An <code>InteractiveCallback</code> which will be used to
\r
207 * determine the responses to the questions asked by the server.
\r
208 * @return whether the connection is now authenticated.
\r
209 * @throws IOException
\r
211 public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
\r
214 return authenticateWithKeyboardInteractive(user, null, cb);
\r
218 * After a successful connect, one has to authenticate oneself. This method
\r
219 * is based on "keyboard-interactive", specified in
\r
220 * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
\r
221 * callback object which will be feeded with challenges generated by the
\r
222 * server. Answers are then sent back to the server. It is possible that the
\r
223 * callback will be called several times during the invocation of this
\r
224 * method (e.g., if the server replies to the callback's answer(s) with
\r
225 * another challenge...)
\r
227 * If the authentication phase is complete, <code>true</code> will be
\r
228 * returned. If the server does not accept the request (or if further
\r
229 * authentication steps are needed), <code>false</code> is returned and
\r
230 * one can retry either by using this or any other authentication method
\r
231 * (use the <code>getRemainingAuthMethods</code> method to get a list of
\r
232 * the remaining possible methods).
\r
234 * Note: some SSH servers advertise "keyboard-interactive", however, any
\r
235 * interactive request will be denied (without having sent any challenge to
\r
239 * A <code>String</code> holding the username.
\r
240 * @param submethods
\r
241 * An array of submethod names, see
\r
242 * draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
\r
243 * to indicate an empty list.
\r
245 * An <code>InteractiveCallback</code> which will be used to
\r
246 * determine the responses to the questions asked by the server.
\r
248 * @return whether the connection is now authenticated.
\r
249 * @throws IOException
\r
251 public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
\r
252 InteractiveCallback cb) throws IOException
\r
255 throw new IllegalArgumentException("Callback may not ne NULL!");
\r
258 throw new IllegalStateException("Connection is not established!");
\r
261 throw new IllegalStateException("Connection is already authenticated!");
\r
264 am = new AuthenticationManager(tm);
\r
267 cm = new ChannelManager(tm);
\r
270 throw new IllegalArgumentException("user argument is null");
\r
272 authenticated = am.authenticateInteractive(user, submethods, cb);
\r
274 return authenticated;
\r
278 * After a successful connect, one has to authenticate oneself. This method
\r
279 * sends username and password to the server.
\r
281 * If the authentication phase is complete, <code>true</code> will be
\r
282 * returned. If the server does not accept the request (or if further
\r
283 * authentication steps are needed), <code>false</code> is returned and
\r
284 * one can retry either by using this or any other authentication method
\r
285 * (use the <code>getRemainingAuthMethods</code> method to get a list of
\r
286 * the remaining possible methods).
\r
288 * Note: if this method fails, then please double-check that it is actually
\r
289 * offered by the server (use
\r
290 * {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
\r
292 * Often, password authentication is disabled, but users are not aware of
\r
293 * it. Many servers only offer "publickey" and "keyboard-interactive".
\r
294 * However, even though "keyboard-interactive" *feels* like password
\r
295 * authentication (e.g., when using the putty or openssh clients) it is
\r
296 * *not* the same mechanism.
\r
300 * @return if the connection is now authenticated.
\r
301 * @throws IOException
\r
303 public synchronized boolean authenticateWithPassword(String user, String password) throws IOException
\r
306 throw new IllegalStateException("Connection is not established!");
\r
309 throw new IllegalStateException("Connection is already authenticated!");
\r
312 am = new AuthenticationManager(tm);
\r
315 cm = new ChannelManager(tm);
\r
318 throw new IllegalArgumentException("user argument is null");
\r
320 if (password == null)
\r
321 throw new IllegalArgumentException("password argument is null");
\r
323 authenticated = am.authenticatePassword(user, password);
\r
325 return authenticated;
\r
329 * After a successful connect, one has to authenticate oneself. This method
\r
330 * can be used to explicitly use the special "none" authentication method
\r
331 * (where only a username has to be specified).
\r
333 * Note 1: The "none" method may always be tried by clients, however as by
\r
334 * the specs, the server will not explicitly announce it. In other words,
\r
335 * the "none" token will never show up in the list returned by
\r
336 * {@link #getRemainingAuthMethods(String)}.
\r
338 * Note 2: no matter which one of the authenticateWithXXX() methods you
\r
339 * call, the library will always issue exactly one initial "none"
\r
340 * authentication request to retrieve the initially allowed list of
\r
341 * authentication methods by the server. Please read RFC 4252 for the
\r
344 * If the authentication phase is complete, <code>true</code> will be
\r
345 * returned. If further authentication steps are needed, <code>false</code>
\r
346 * is returned and one can retry by any other authentication method (use the
\r
347 * <code>getRemainingAuthMethods</code> method to get a list of the
\r
348 * remaining possible methods).
\r
351 * @return if the connection is now authenticated.
\r
352 * @throws IOException
\r
354 public synchronized boolean authenticateWithNone(String user) throws IOException
\r
357 throw new IllegalStateException("Connection is not established!");
\r
360 throw new IllegalStateException("Connection is already authenticated!");
\r
363 am = new AuthenticationManager(tm);
\r
366 cm = new ChannelManager(tm);
\r
369 throw new IllegalArgumentException("user argument is null");
\r
371 /* Trigger the sending of the PacketUserauthRequestNone packet */
\r
372 /* (if not already done) */
\r
374 authenticated = am.authenticateNone(user);
\r
376 return authenticated;
\r
380 * After a successful connect, one has to authenticate oneself. The
\r
381 * authentication method "publickey" works by signing a challenge sent by
\r
382 * the server. The signature is either DSA or RSA based - it just depends on
\r
383 * the type of private key you specify, either a DSA or RSA private key in
\r
384 * PEM format. And yes, this is may seem to be a little confusing, the
\r
385 * method is called "publickey" in the SSH-2 protocol specification, however
\r
386 * since we need to generate a signature, you actually have to supply a
\r
389 * The private key contained in the PEM file may also be encrypted
\r
390 * ("Proc-Type: 4,ENCRYPTED"). The library supports DES-CBC and DES-EDE3-CBC
\r
391 * encryption, as well as the more exotic PEM encrpytions AES-128-CBC,
\r
392 * AES-192-CBC and AES-256-CBC.
\r
394 * If the authentication phase is complete, <code>true</code> will be
\r
395 * returned. If the server does not accept the request (or if further
\r
396 * authentication steps are needed), <code>false</code> is returned and
\r
397 * one can retry either by using this or any other authentication method
\r
398 * (use the <code>getRemainingAuthMethods</code> method to get a list of
\r
399 * the remaining possible methods).
\r
401 * NOTE PUTTY USERS: Event though your key file may start with
\r
402 * "-----BEGIN..." it is not in the expected format. You have to convert it
\r
403 * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
\r
404 * from the Putty website). Simply load your key and then use the
\r
405 * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
\r
408 * A <code>String</code> holding the username.
\r
409 * @param pemPrivateKey
\r
410 * A <code>char[]</code> containing a DSA or RSA private key of
\r
411 * the user in OpenSSH key format (PEM, you can't miss the
\r
412 * "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE
\r
413 * KEY-----" tag). The char array may contain
\r
414 * linebreaks/linefeeds.
\r
416 * If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED")
\r
417 * then you must specify a password. Otherwise, this argument
\r
418 * will be ignored and can be set to <code>null</code>.
\r
420 * @return whether the connection is now authenticated.
\r
421 * @throws IOException
\r
423 public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
\r
427 throw new IllegalStateException("Connection is not established!");
\r
430 throw new IllegalStateException("Connection is already authenticated!");
\r
433 am = new AuthenticationManager(tm);
\r
436 cm = new ChannelManager(tm);
\r
439 throw new IllegalArgumentException("user argument is null");
\r
441 if (pemPrivateKey == null)
\r
442 throw new IllegalArgumentException("pemPrivateKey argument is null");
\r
444 authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
\r
446 return authenticated;
\r
450 * After a successful connect, one has to authenticate oneself. The
\r
451 * authentication method "publickey" works by signing a challenge sent by
\r
452 * the server. The signature is either DSA or RSA based - it just depends on
\r
453 * the type of private key you specify, either a DSA or RSA private key in
\r
454 * PEM format. And yes, this is may seem to be a little confusing, the
\r
455 * method is called "publickey" in the SSH-2 protocol specification, however
\r
456 * since we need to generate a signature, you actually have to supply a
\r
459 * If the authentication phase is complete, <code>true</code> will be
\r
460 * returned. If the server does not accept the request (or if further
\r
461 * authentication steps are needed), <code>false</code> is returned and
\r
462 * one can retry either by using this or any other authentication method
\r
463 * (use the <code>getRemainingAuthMethods</code> method to get a list of
\r
464 * the remaining possible methods).
\r
467 * A <code>String</code> holding the username.
\r
469 * A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
\r
470 * containing a DSA or RSA private key of
\r
471 * the user in Trilead object format.
\r
473 * @return whether the connection is now authenticated.
\r
474 * @throws IOException
\r
476 public synchronized boolean authenticateWithPublicKey(String user, Object key)
\r
480 throw new IllegalStateException("Connection is not established!");
\r
483 throw new IllegalStateException("Connection is already authenticated!");
\r
486 am = new AuthenticationManager(tm);
\r
489 cm = new ChannelManager(tm);
\r
492 throw new IllegalArgumentException("user argument is null");
\r
495 throw new IllegalArgumentException("Key argument is null");
\r
497 authenticated = am.authenticatePublicKey(user, key, getOrCreateSecureRND());
\r
499 return authenticated;
\r
502 * A convenience wrapper function which reads in a private key (PEM format,
\r
503 * either DSA or RSA) and then calls
\r
504 * <code>authenticateWithPublicKey(String, char[], String)</code>.
\r
506 * NOTE PUTTY USERS: Event though your key file may start with
\r
507 * "-----BEGIN..." it is not in the expected format. You have to convert it
\r
508 * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
\r
509 * from the Putty website). Simply load your key and then use the
\r
510 * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
\r
513 * A <code>String</code> holding the username.
\r
515 * A <code>File</code> object pointing to a file containing a
\r
516 * DSA or RSA private key of the user in OpenSSH key format (PEM,
\r
517 * you can't miss the "-----BEGIN DSA PRIVATE KEY-----" or
\r
518 * "-----BEGIN RSA PRIVATE KEY-----" tag).
\r
520 * If the PEM file is encrypted then you must specify the
\r
521 * password. Otherwise, this argument will be ignored and can be
\r
522 * set to <code>null</code>.
\r
524 * @return whether the connection is now authenticated.
\r
525 * @throws IOException
\r
527 public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
\r
530 if (pemFile == null)
\r
531 throw new IllegalArgumentException("pemFile argument is null");
\r
533 char[] buff = new char[256];
\r
535 CharArrayWriter cw = new CharArrayWriter();
\r
537 FileReader fr = new FileReader(pemFile);
\r
541 int len = fr.read(buff);
\r
544 cw.write(buff, 0, len);
\r
549 return authenticateWithPublicKey(user, cw.toCharArray(), password);
\r
553 * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any
\r
554 * time, but it is best to add connection monitors before invoking
\r
555 * <code>connect()</code> to avoid glitches (e.g., you add a connection
\r
556 * monitor after a successful connect(), but the connection has died in the
\r
557 * mean time. Then, your connection monitor won't be notified.)
\r
559 * You can add as many monitors as you like.
\r
561 * @see ConnectionMonitor
\r
564 * An object implementing the <code>ConnectionMonitor</code>
\r
567 public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
\r
570 throw new IllegalArgumentException("cmon argument is null");
\r
572 connectionMonitors.addElement(cmon);
\r
575 tm.setConnectionMonitors(connectionMonitors);
\r
579 * Controls whether compression is used on the link or not.
\r
581 * Note: This can only be called before connect()
\r
582 * @param enabled whether to enable compression
\r
583 * @throws IOException
\r
585 public synchronized void setCompression(boolean enabled) throws IOException {
\r
587 throw new IOException("Connection to " + hostname + " is already in connected state!");
\r
589 compression = enabled;
\r
593 * Close the connection to the SSH-2 server. All assigned sessions will be
\r
594 * closed, too. Can be called at any time. Don't forget to call this once
\r
595 * you don't need a connection anymore - otherwise the receiver thread may
\r
598 public synchronized void close()
\r
600 Throwable t = new Throwable("Closed due to user request.");
\r
604 private void close(Throwable t, boolean hard)
\r
607 cm.closeAllChannels();
\r
611 tm.close(t, hard == false);
\r
616 authenticated = false;
\r
621 * {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
\r
623 * @return see comments for the
\r
624 * {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
\r
626 * @throws IOException
\r
628 public synchronized ConnectionInfo connect() throws IOException
\r
630 return connect(null, 0, 0);
\r
635 * {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
\r
637 * @return see comments for the
\r
638 * {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
\r
640 * @throws IOException
\r
642 public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
\r
644 return connect(verifier, 0, 0);
\r
648 * Connect to the SSH-2 server and, as soon as the server has presented its
\r
649 * host key, use the
\r
650 * {@link ServerHostKeyVerifier#verifyServerHostKey(String, int, String,
\r
651 * byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method of the
\r
652 * <code>verifier</code> to ask for permission to proceed. If
\r
653 * <code>verifier</code> is <code>null</code>, then any host key will
\r
654 * be accepted - this is NOT recommended, since it makes man-in-the-middle
\r
655 * attackes VERY easy (somebody could put a proxy SSH server between you and
\r
656 * the real server).
\r
658 * Note: The verifier will be called before doing any crypto calculations
\r
659 * (i.e., diffie-hellman). Therefore, if you don't like the presented host
\r
660 * key then no CPU cycles are wasted (and the evil server has less
\r
661 * information about us).
\r
663 * However, it is still possible that the server presented a fake host key:
\r
664 * the server cheated (typically a sign for a man-in-the-middle attack) and
\r
665 * is not able to generate a signature that matches its host key. Don't
\r
666 * worry, the library will detect such a scenario later when checking the
\r
667 * signature (the signature cannot be checked before having completed the
\r
668 * diffie-hellman exchange).
\r
670 * Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String, int,
\r
671 * String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method will
\r
672 * *NOT* be called from the current thread, the call is being made from a
\r
673 * background thread (there is a background dispatcher thread for every
\r
674 * established connection).
\r
676 * Note 3: This method will block as long as the key exchange of the
\r
677 * underlying connection has not been completed (and you have not specified
\r
680 * Note 4: If you want to re-use a connection object that was successfully
\r
681 * connected, then you must call the {@link #close()} method before invoking
\r
682 * <code>connect()</code> again.
\r
685 * An object that implements the {@link ServerHostKeyVerifier}
\r
686 * interface. Pass <code>null</code> to accept any server host
\r
687 * key - NOT recommended.
\r
689 * @param connectTimeout
\r
690 * Connect the underlying TCP socket to the server with the given
\r
691 * timeout value (non-negative, in milliseconds). Zero means no
\r
692 * timeout. If a proxy is being used (see
\r
693 * {@link #setProxyData(ProxyData)}), then this timeout is used
\r
694 * for the connection establishment to the proxy.
\r
696 * @param kexTimeout
\r
697 * Timeout for complete connection establishment (non-negative,
\r
698 * in milliseconds). Zero means no timeout. The timeout counts
\r
699 * from the moment you invoke the connect() method and is
\r
700 * cancelled as soon as the first key-exchange round has
\r
701 * finished. It is possible that the timeout event will be fired
\r
702 * during the invocation of the <code>verifier</code> callback,
\r
703 * but it will only have an effect after the
\r
704 * <code>verifier</code> returns.
\r
706 * @return A {@link ConnectionInfo} object containing the details of the
\r
707 * established connection.
\r
709 * @throws IOException
\r
710 * If any problem occurs, e.g., the server's host key is not
\r
711 * accepted by the <code>verifier</code> or there is problem
\r
712 * during the initial crypto setup (e.g., the signature sent by
\r
713 * the server is wrong).
\r
715 * In case of a timeout (either connectTimeout or kexTimeout) a
\r
716 * SocketTimeoutException is thrown.
\r
718 * An exception may also be thrown if the connection was already
\r
719 * successfully connected (no matter if the connection broke in
\r
720 * the mean time) and you invoke <code>connect()</code> again
\r
721 * without having called {@link #close()} first.
\r
723 * If a HTTP proxy is being used and the proxy refuses the
\r
724 * connection, then a {@link HTTPProxyException} may be thrown,
\r
725 * which contains the details returned by the proxy. If the
\r
726 * proxy is buggy and does not return a proper HTTP response,
\r
727 * then a normal IOException is thrown instead.
\r
729 public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
\r
732 final class TimeoutState
\r
734 boolean isCancelled = false;
\r
735 boolean timeoutSocketClosed = false;
\r
739 throw new IOException("Connection to " + hostname + " is already in connected state!");
\r
741 if (connectTimeout < 0)
\r
742 throw new IllegalArgumentException("connectTimeout must be non-negative!");
\r
744 if (kexTimeout < 0)
\r
745 throw new IllegalArgumentException("kexTimeout must be non-negative!");
\r
747 final TimeoutState state = new TimeoutState();
\r
749 tm = new TransportManager(hostname, port);
\r
751 tm.setConnectionMonitors(connectionMonitors);
\r
753 // Don't offer compression if not requested
\r
754 if (!compression) {
\r
755 cryptoWishList.c2s_comp_algos = new String[] { "none" };
\r
756 cryptoWishList.s2c_comp_algos = new String[] { "none" };
\r
760 * Make sure that the runnable below will observe the new value of "tm"
\r
761 * and "state" (the runnable will be executed in a different thread,
\r
762 * which may be already running, that is why we need a memory barrier
\r
763 * here). See also the comment in Channel.java if you are interested in
\r
766 * OKOK, this is paranoid since adding the runnable to the todo list of
\r
767 * the TimeoutService will ensure that all writes have been flushed
\r
768 * before the Runnable reads anything (there is a synchronized block in
\r
769 * TimeoutService.addTimeoutHandler).
\r
774 /* We could actually synchronize on anything. */
\r
779 TimeoutToken token = null;
\r
781 if (kexTimeout > 0)
\r
783 final Runnable timeoutHandler = new Runnable()
\r
787 synchronized (state)
\r
789 if (state.isCancelled)
\r
791 state.timeoutSocketClosed = true;
\r
792 tm.close(new SocketTimeoutException("The connect timeout expired"), false);
\r
797 long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
\r
799 token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
\r
804 tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
\r
806 catch (SocketTimeoutException se)
\r
808 throw (SocketTimeoutException) new SocketTimeoutException(
\r
809 "The connect() operation on the socket timed out.").initCause(se);
\r
812 tm.setTcpNoDelay(tcpNoDelay);
\r
814 /* Wait until first KEX has finished */
\r
816 ConnectionInfo ci = tm.getConnectionInfo(1);
\r
818 /* Now try to cancel the timeout, if needed */
\r
822 TimeoutService.cancelTimeoutHandler(token);
\r
824 /* Were we too late? */
\r
826 synchronized (state)
\r
828 if (state.timeoutSocketClosed)
\r
829 throw new IOException("This exception will be replaced by the one below =)");
\r
831 * Just in case the "cancelTimeoutHandler" invocation came
\r
832 * just a little bit too late but the handler did not enter
\r
833 * the semaphore yet - we can still stop it.
\r
835 state.isCancelled = true;
\r
841 catch (SocketTimeoutException ste)
\r
845 catch (IOException e1)
\r
847 /* This will also invoke any registered connection monitors */
\r
848 close(new Throwable("There was a problem during connect."), false);
\r
850 synchronized (state)
\r
853 * Show a clean exception, not something like "the socket is
\r
856 if (state.timeoutSocketClosed)
\r
857 throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
\r
860 /* Do not wrap a HTTPProxyException */
\r
861 if (e1 instanceof HTTPProxyException)
\r
864 throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
\r
870 * Creates a new {@link LocalPortForwarder}. A
\r
871 * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
\r
872 * at a local port via the secure tunnel to another host (which may or may
\r
873 * not be identical to the remote SSH-2 server).
\r
875 * This method must only be called after one has passed successfully the
\r
876 * authentication step. There is no limit on the number of concurrent
\r
879 * @param local_port
\r
880 * the local port the LocalPortForwarder shall bind to.
\r
881 * @param host_to_connect
\r
882 * target address (IP or hostname)
\r
883 * @param port_to_connect
\r
885 * @return A {@link LocalPortForwarder} object.
\r
886 * @throws IOException
\r
888 public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
\r
889 int port_to_connect) throws IOException
\r
892 throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
\r
894 if (!authenticated)
\r
895 throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
\r
897 return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
\r
901 * Creates a new {@link LocalPortForwarder}. A
\r
902 * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
\r
903 * at a local port via the secure tunnel to another host (which may or may
\r
904 * not be identical to the remote SSH-2 server).
\r
906 * This method must only be called after one has passed successfully the
\r
907 * authentication step. There is no limit on the number of concurrent
\r
911 * specifies the InetSocketAddress where the local socket shall
\r
913 * @param host_to_connect
\r
914 * target address (IP or hostname)
\r
915 * @param port_to_connect
\r
917 * @return A {@link LocalPortForwarder} object.
\r
918 * @throws IOException
\r
920 public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
\r
921 int port_to_connect) throws IOException
\r
924 throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
\r
926 if (!authenticated)
\r
927 throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
\r
929 return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
\r
933 * Creates a new {@link LocalStreamForwarder}. A
\r
934 * <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
\r
935 * that is being forwarded via the secure tunnel into a TCP/IP connection to
\r
936 * another host (which may or may not be identical to the remote SSH-2
\r
939 * @param host_to_connect
\r
940 * @param port_to_connect
\r
941 * @return A {@link LocalStreamForwarder} object.
\r
942 * @throws IOException
\r
944 public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
\r
948 throw new IllegalStateException("Cannot forward, you need to establish a connection first.");
\r
950 if (!authenticated)
\r
951 throw new IllegalStateException("Cannot forward, connection is not authenticated.");
\r
953 return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
\r
957 * Creates a new {@link DynamicPortForwarder}. A
\r
958 * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
\r
959 * at a local port via the secure tunnel to another host that is chosen via
\r
960 * the SOCKS protocol.
\r
962 * This method must only be called after one has passed successfully the
\r
963 * authentication step. There is no limit on the number of concurrent
\r
966 * @param local_port
\r
967 * @return A {@link DynamicPortForwarder} object.
\r
968 * @throws IOException
\r
970 public synchronized DynamicPortForwarder createDynamicPortForwarder(int local_port) throws IOException
\r
973 throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
\r
975 if (!authenticated)
\r
976 throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
\r
978 return new DynamicPortForwarder(cm, local_port);
\r
982 * Creates a new {@link DynamicPortForwarder}. A
\r
983 * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
\r
984 * at a local port via the secure tunnel to another host that is chosen via
\r
985 * the SOCKS protocol.
\r
987 * This method must only be called after one has passed successfully the
\r
988 * authentication step. There is no limit on the number of concurrent
\r
992 * specifies the InetSocketAddress where the local socket shall
\r
994 * @return A {@link DynamicPortForwarder} object.
\r
995 * @throws IOException
\r
997 public synchronized DynamicPortForwarder createDynamicPortForwarder(InetSocketAddress addr) throws IOException
\r
1000 throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
\r
1002 if (!authenticated)
\r
1003 throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
\r
1005 return new DynamicPortForwarder(cm, addr);
\r
1009 * Create a very basic {@link SCPClient} that can be used to copy files
\r
1010 * from/to the SSH-2 server.
\r
1012 * Works only after one has passed successfully the authentication step.
\r
1013 * There is no limit on the number of concurrent SCP clients.
\r
1015 * Note: This factory method will probably disappear in the future.
\r
1017 * @return A {@link SCPClient} object.
\r
1018 * @throws IOException
\r
1020 public synchronized SCPClient createSCPClient() throws IOException
\r
1023 throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");
\r
1025 if (!authenticated)
\r
1026 throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");
\r
1028 return new SCPClient(this);
\r
1032 * Force an asynchronous key re-exchange (the call does not block). The
\r
1033 * latest values set for MAC, Cipher and DH group exchange parameters will
\r
1034 * be used. If a key exchange is currently in progress, then this method has
\r
1035 * the only effect that the so far specified parameters will be used for the
\r
1036 * next (server driven) key exchange.
\r
1038 * Note: This implementation will never start a key exchange (other than the
\r
1039 * initial one) unless you or the SSH-2 server ask for it.
\r
1041 * @throws IOException
\r
1042 * In case of any failure behind the scenes.
\r
1044 public synchronized void forceKeyExchange() throws IOException
\r
1047 throw new IllegalStateException("You need to establish a connection first.");
\r
1049 tm.forceKeyExchange(cryptoWishList, dhgexpara);
\r
1053 * Returns the hostname that was passed to the constructor.
\r
1055 * @return the hostname
\r
1057 public synchronized String getHostname()
\r
1063 * Returns the port that was passed to the constructor.
\r
1065 * @return the TCP port
\r
1067 public synchronized int getPort()
\r
1073 * Returns a {@link ConnectionInfo} object containing the details of the
\r
1074 * connection. Can be called as soon as the connection has been established
\r
1075 * (successfully connected).
\r
1077 * @return A {@link ConnectionInfo} object.
\r
1078 * @throws IOException
\r
1079 * In case of any failure behind the scenes.
\r
1081 public synchronized ConnectionInfo getConnectionInfo() throws IOException
\r
1084 throw new IllegalStateException(
\r
1085 "Cannot get details of connection, you need to establish a connection first.");
\r
1086 return tm.getConnectionInfo(1);
\r
1090 * After a successful connect, one has to authenticate oneself. This method
\r
1091 * can be used to tell which authentication methods are supported by the
\r
1092 * server at a certain stage of the authentication process (for the given
\r
1095 * Note 1: the username will only be used if no authentication step was done
\r
1096 * so far (it will be used to ask the server for a list of possible
\r
1097 * authentication methods by sending the initial "none" request). Otherwise,
\r
1098 * this method ignores the user name and returns a cached method list (which
\r
1099 * is based on the information contained in the last negative server
\r
1102 * Note 2: the server may return method names that are not supported by this
\r
1105 * After a successful authentication, this method must not be called
\r
1109 * A <code>String</code> holding the username.
\r
1111 * @return a (possibly emtpy) array holding authentication method names.
\r
1112 * @throws IOException
\r
1114 public synchronized String[] getRemainingAuthMethods(String user) throws IOException
\r
1117 throw new IllegalArgumentException("user argument may not be NULL!");
\r
1120 throw new IllegalStateException("Connection is not established!");
\r
1122 if (authenticated)
\r
1123 throw new IllegalStateException("Connection is already authenticated!");
\r
1126 am = new AuthenticationManager(tm);
\r
1129 cm = new ChannelManager(tm);
\r
1131 return am.getRemainingMethods(user);
\r
1135 * Determines if the authentication phase is complete. Can be called at any
\r
1138 * @return <code>true</code> if no further authentication steps are
\r
1141 public synchronized boolean isAuthenticationComplete()
\r
1143 return authenticated;
\r
1147 * Returns true if there was at least one failed authentication request and
\r
1148 * the last failed authentication request was marked with "partial success"
\r
1149 * by the server. This is only needed in the rare case of SSH-2 server
\r
1150 * setups that cannot be satisfied with a single successful authentication
\r
1151 * request (i.e., multiple authentication steps are needed.)
\r
1153 * If you are interested in the details, then have a look at RFC4252.
\r
1155 * @return if the there was a failed authentication step and the last one
\r
1156 * was marked as a "partial success".
\r
1158 public synchronized boolean isAuthenticationPartialSuccess()
\r
1163 return am.getPartialSuccess();
\r
1167 * Checks if a specified authentication method is available. This method is
\r
1168 * actually just a wrapper for {@link #getRemainingAuthMethods(String)
\r
1169 * getRemainingAuthMethods()}.
\r
1172 * A <code>String</code> holding the username.
\r
1174 * An authentication method name (e.g., "publickey", "password",
\r
1175 * "keyboard-interactive") as specified by the SSH-2 standard.
\r
1176 * @return if the specified authentication method is currently available.
\r
1177 * @throws IOException
\r
1179 public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException
\r
1181 if (method == null)
\r
1182 throw new IllegalArgumentException("method argument may not be NULL!");
\r
1184 String methods[] = getRemainingAuthMethods(user);
\r
1186 for (int i = 0; i < methods.length; i++)
\r
1188 if (methods[i].compareTo(method) == 0)
\r
1195 private final SecureRandom getOrCreateSecureRND()
\r
1197 if (generator == null)
\r
1198 generator = new SecureRandom();
\r
1204 * Open a new {@link Session} on this connection. Works only after one has
\r
1205 * passed successfully the authentication step. There is no limit on the
\r
1206 * number of concurrent sessions.
\r
1208 * @return A {@link Session} object.
\r
1209 * @throws IOException
\r
1211 public synchronized Session openSession() throws IOException
\r
1214 throw new IllegalStateException("Cannot open session, you need to establish a connection first.");
\r
1216 if (!authenticated)
\r
1217 throw new IllegalStateException("Cannot open session, connection is not authenticated.");
\r
1219 return new Session(cm, getOrCreateSecureRND());
\r
1223 * Send an SSH_MSG_IGNORE packet. This method will generate a random data
\r
1224 * attribute (length between 0 (invlusive) and 16 (exclusive) bytes,
\r
1225 * contents are random bytes).
\r
1227 * This method must only be called once the connection is established.
\r
1229 * @throws IOException
\r
1231 public synchronized void sendIgnorePacket() throws IOException
\r
1233 SecureRandom rnd = getOrCreateSecureRND();
\r
1235 byte[] data = new byte[rnd.nextInt(16)];
\r
1236 rnd.nextBytes(data);
\r
1238 sendIgnorePacket(data);
\r
1242 * Send an SSH_MSG_IGNORE packet with the given data attribute.
\r
1244 * This method must only be called once the connection is established.
\r
1246 * @throws IOException
\r
1248 public synchronized void sendIgnorePacket(byte[] data) throws IOException
\r
1251 throw new IllegalArgumentException("data argument must not be null.");
\r
1254 throw new IllegalStateException(
\r
1255 "Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first.");
\r
1257 PacketIgnore pi = new PacketIgnore();
\r
1260 tm.sendMessage(pi.getPayload());
\r
1264 * Removes duplicates from a String array, keeps only first occurence of
\r
1265 * each element. Does not destroy order of elements; can handle nulls. Uses
\r
1266 * a very efficient O(N^2) algorithm =)
\r
1270 * @return a cleaned String array.
\r
1272 private String[] removeDuplicates(String[] list)
\r
1274 if ((list == null) || (list.length < 2))
\r
1277 String[] list2 = new String[list.length];
\r
1281 for (int i = 0; i < list.length; i++)
\r
1283 boolean duplicate = false;
\r
1285 String element = list[i];
\r
1287 for (int j = 0; j < count; j++)
\r
1289 if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j]))))
\r
1299 list2[count++] = list[i];
\r
1302 if (count == list2.length)
\r
1305 String[] tmp = new String[count];
\r
1306 System.arraycopy(list2, 0, tmp, 0, count);
\r
1312 * Unless you know what you are doing, you will never need this.
\r
1316 public synchronized void setClient2ServerCiphers(String[] ciphers)
\r
1318 if ((ciphers == null) || (ciphers.length == 0))
\r
1319 throw new IllegalArgumentException();
\r
1320 ciphers = removeDuplicates(ciphers);
\r
1321 BlockCipherFactory.checkCipherList(ciphers);
\r
1322 cryptoWishList.c2s_enc_algos = ciphers;
\r
1326 * Unless you know what you are doing, you will never need this.
\r
1330 public synchronized void setClient2ServerMACs(String[] macs)
\r
1332 if ((macs == null) || (macs.length == 0))
\r
1333 throw new IllegalArgumentException();
\r
1334 macs = removeDuplicates(macs);
\r
1335 MAC.checkMacList(macs);
\r
1336 cryptoWishList.c2s_mac_algos = macs;
\r
1340 * Sets the parameters for the diffie-hellman group exchange. Unless you
\r
1341 * know what you are doing, you will never need this. Default values are
\r
1342 * defined in the {@link DHGexParameters} class.
\r
1345 * {@link DHGexParameters}, non null.
\r
1348 public synchronized void setDHGexParameters(DHGexParameters dgp)
\r
1351 throw new IllegalArgumentException();
\r
1357 * Unless you know what you are doing, you will never need this.
\r
1361 public synchronized void setServer2ClientCiphers(String[] ciphers)
\r
1363 if ((ciphers == null) || (ciphers.length == 0))
\r
1364 throw new IllegalArgumentException();
\r
1365 ciphers = removeDuplicates(ciphers);
\r
1366 BlockCipherFactory.checkCipherList(ciphers);
\r
1367 cryptoWishList.s2c_enc_algos = ciphers;
\r
1371 * Unless you know what you are doing, you will never need this.
\r
1375 public synchronized void setServer2ClientMACs(String[] macs)
\r
1377 if ((macs == null) || (macs.length == 0))
\r
1378 throw new IllegalArgumentException();
\r
1380 macs = removeDuplicates(macs);
\r
1381 MAC.checkMacList(macs);
\r
1382 cryptoWishList.s2c_mac_algos = macs;
\r
1386 * Define the set of allowed server host key algorithms to be used for the
\r
1387 * following key exchange operations.
\r
1389 * Unless you know what you are doing, you will never need this.
\r
1392 * An array of allowed server host key algorithms. SSH-2 defines
\r
1393 * <code>ssh-dss</code> and <code>ssh-rsa</code>. The
\r
1394 * entries of the array must be ordered after preference, i.e.,
\r
1395 * the entry at index 0 is the most preferred one. You must
\r
1396 * specify at least one entry.
\r
1398 public synchronized void setServerHostKeyAlgorithms(String[] algos)
\r
1400 if ((algos == null) || (algos.length == 0))
\r
1401 throw new IllegalArgumentException();
\r
1403 algos = removeDuplicates(algos);
\r
1404 KexManager.checkServerHostkeyAlgorithmsList(algos);
\r
1405 cryptoWishList.serverHostKeyAlgorithms = algos;
\r
1409 * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the
\r
1410 * underlying socket.
\r
1412 * Can be called at any time. If the connection has not yet been established
\r
1413 * then the passed value will be stored and set after the socket has been
\r
1414 * set up. The default value that will be used is <code>false</code>.
\r
1417 * the argument passed to the <code>Socket.setTCPNoDelay()</code>
\r
1419 * @throws IOException
\r
1421 public synchronized void setTCPNoDelay(boolean enable) throws IOException
\r
1423 tcpNoDelay = enable;
\r
1426 tm.setTcpNoDelay(enable);
\r
1430 * Used to tell the library that the connection shall be established through
\r
1431 * a proxy server. It only makes sense to call this method before calling
\r
1432 * the {@link #connect() connect()} method.
\r
1434 * At the moment, only HTTP proxies are supported.
\r
1436 * Note: This method can be called any number of times. The
\r
1437 * {@link #connect() connect()} method will use the value set in the last
\r
1438 * preceding invocation of this method.
\r
1440 * @see HTTPProxyData
\r
1442 * @param proxyData
\r
1443 * Connection information about the proxy. If <code>null</code>,
\r
1444 * then no proxy will be used (non surprisingly, this is also the
\r
1447 public synchronized void setProxyData(ProxyData proxyData)
\r
1449 this.proxyData = proxyData;
\r
1453 * Request a remote port forwarding. If successful, then forwarded
\r
1454 * connections will be redirected to the given target address. You can
\r
1455 * cancle a requested remote port forwarding by calling
\r
1456 * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
\r
1458 * A call of this method will block until the peer either agreed or
\r
1459 * disagreed to your request-
\r
1461 * Note 1: this method typically fails if you
\r
1463 * <li>pass a port number for which the used remote user has not enough
\r
1464 * permissions (i.e., port < 1024)</li>
\r
1465 * <li>or pass a port number that is already in use on the remote server</li>
\r
1466 * <li>or if remote port forwarding is disabled on the server.</li>
\r
1469 * Note 2: (from the openssh man page): By default, the listening socket on
\r
1470 * the server will be bound to the loopback interface only. This may be
\r
1471 * overriden by specifying a bind address. Specifying a remote bind address
\r
1472 * will only succeed if the server's <b>GatewayPorts</b> option is enabled
\r
1473 * (see sshd_config(5)).
\r
1475 * @param bindAddress
\r
1476 * address to bind to on the server:
\r
1478 * <li>"" means that connections are to be accepted on all
\r
1479 * protocol families supported by the SSH implementation</li>
\r
1480 * <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
\r
1481 * <li>"::" means to listen on all IPv6 addresses</li>
\r
1482 * <li>"localhost" means to listen on all protocol families
\r
1483 * supported by the SSH implementation on loopback addresses
\r
1484 * only, [RFC3330] and RFC3513]</li>
\r
1485 * <li>"127.0.0.1" and "::1" indicate listening on the loopback
\r
1486 * interfaces for IPv4 and IPv6 respectively</li>
\r
1489 * port number to bind on the server (must be > 0)
\r
1490 * @param targetAddress
\r
1491 * the target address (IP or hostname)
\r
1492 * @param targetPort
\r
1494 * @throws IOException
\r
1496 public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
\r
1497 int targetPort) throws IOException
\r
1500 throw new IllegalStateException("You need to establish a connection first.");
\r
1502 if (!authenticated)
\r
1503 throw new IllegalStateException("The connection is not authenticated.");
\r
1505 if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
\r
1506 throw new IllegalArgumentException();
\r
1508 cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
\r
1512 * Cancel an earlier requested remote port forwarding. Currently active
\r
1513 * forwardings will not be affected (e.g., disrupted). Note that further
\r
1514 * connection forwarding requests may be received until this method has
\r
1518 * the allocated port number on the server
\r
1519 * @throws IOException
\r
1520 * if the remote side refuses the cancel request or another low
\r
1521 * level error occurs (e.g., the underlying connection is
\r
1524 public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
\r
1527 throw new IllegalStateException("You need to establish a connection first.");
\r
1529 if (!authenticated)
\r
1530 throw new IllegalStateException("The connection is not authenticated.");
\r
1532 cm.requestCancelGlobalForward(bindPort);
\r
1536 * Provide your own instance of SecureRandom. Can be used, e.g., if you want
\r
1537 * to seed the used SecureRandom generator manually.
\r
1539 * The SecureRandom instance is used during key exchanges, public key
\r
1540 * authentication, x11 cookie generation and the like.
\r
1543 * a SecureRandom instance
\r
1545 public synchronized void setSecureRandom(SecureRandom rnd)
\r
1548 throw new IllegalArgumentException();
\r
1550 this.generator = rnd;
\r
1554 * Enable/disable debug logging. <b>Only do this when requested by Trilead
\r
1557 * For speed reasons, some static variables used to check whether debugging
\r
1558 * is enabled are not protected with locks. In other words, if you
\r
1559 * dynamicaly enable/disable debug logging, then some threads may still use
\r
1560 * the old setting. To be on the safe side, enable debugging before doing
\r
1561 * the <code>connect()</code> call.
\r
1566 * a {@link DebugLogger DebugLogger} instance, <code>null</code>
\r
1567 * means logging using the simple logger which logs all messages
\r
1568 * to to stderr. Ignored if enabled is <code>false</code>
\r
1570 public synchronized void enableDebugging(boolean enable, DebugLogger logger)
\r
1572 Logger.enabled = enable;
\r
1574 if (enable == false)
\r
1576 Logger.logger = null;
\r
1580 if (logger == null)
\r
1582 logger = new DebugLogger()
\r
1585 public void log(int level, String className, String message)
\r
1587 long now = System.currentTimeMillis();
\r
1588 System.err.println(now + " : " + className + ": " + message);
\r
1593 Logger.logger = logger;
\r
1598 * This method can be used to perform end-to-end connection testing. It
\r
1599 * sends a 'ping' message to the server and waits for the 'pong' from the
\r
1602 * When this method throws an exception, then you can assume that the
\r
1603 * connection should be abandoned.
\r
1605 * Note: Works only after one has passed successfully the authentication
\r
1608 * Implementation details: this method sends a SSH_MSG_GLOBAL_REQUEST
\r
1609 * request ('trilead-ping') to the server and waits for the
\r
1610 * SSH_MSG_REQUEST_FAILURE reply packet from the server.
\r
1612 * @throws IOException
\r
1613 * in case of any problem
\r
1615 public synchronized void ping() throws IOException
\r
1618 throw new IllegalStateException("You need to establish a connection first.");
\r
1620 if (!authenticated)
\r
1621 throw new IllegalStateException("The connection is not authenticated.");
\r
1623 cm.requestGlobalTrileadPing();
\r