• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
3  * Please refer to the LICENSE.txt for licensing details.
4  */
5 package ch.ethz.ssh2;
6 
7 import java.io.CharArrayWriter;
8 import java.io.File;
9 import java.io.FileReader;
10 import java.io.IOException;
11 import java.net.InetSocketAddress;
12 import java.net.SocketTimeoutException;
13 import java.security.SecureRandom;
14 import java.util.List;
15 import java.util.Vector;
16 
17 import ch.ethz.ssh2.auth.AuthenticationManager;
18 import ch.ethz.ssh2.channel.ChannelManager;
19 import ch.ethz.ssh2.crypto.CryptoWishList;
20 import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
21 import ch.ethz.ssh2.crypto.digest.MAC;
22 import ch.ethz.ssh2.packets.PacketIgnore;
23 import ch.ethz.ssh2.transport.KexManager;
24 import ch.ethz.ssh2.transport.TransportManager;
25 import ch.ethz.ssh2.util.TimeoutService;
26 import ch.ethz.ssh2.util.TimeoutService.TimeoutToken;
27 
28 /**
29  * A <code>Connection</code> is used to establish an encrypted TCP/IP
30  * connection to a SSH-2 server.
31  * <p>
32  * Typically, one
33  * <ol>
34  * <li>creates a {@link #Connection(String) Connection} object.</li>
35  * <li>calls the {@link #connect() connect()} method.</li>
36  * <li>calls some of the authentication methods (e.g., {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
37  * <li>calls one or several times the {@link #openSession() openSession()} method.</li>
38  * <li>finally, one must close the connection and release resources with the {@link #close() close()} method.</li>
39  * </ol>
40  *
41  * @author Christian Plattner
42  * @version $Id: Connection.java 37 2011-05-28 22:31:46Z dkocher@sudo.ch $
43  */
44 
45 public class Connection
46 {
47 	/**
48 	 * The identifier presented to the SSH-2 server.
49 	 */
50 	private String identification = "Ganymed-" + Version.getSpecification();
51 
52 	/* Will be used to generate all random data needed for the current connection.
53 	 * Note: SecureRandom.nextBytes() is thread safe.
54 	 */
55 
56 	private SecureRandom generator;
57 
58 	/**
59 	 * Unless you know what you are doing, you will never need this.
60 	 *
61 	 * @return The list of supported cipher algorithms by this implementation.
62 	 */
getAvailableCiphers()63 	public static synchronized String[] getAvailableCiphers()
64 	{
65 		return BlockCipherFactory.getDefaultCipherList();
66 	}
67 
68 	/**
69 	 * Unless you know what you are doing, you will never need this.
70 	 *
71 	 * @return The list of supported MAC algorthims by this implementation.
72 	 */
getAvailableMACs()73 	public static synchronized String[] getAvailableMACs()
74 	{
75 		return MAC.getMacList();
76 	}
77 
78 	/**
79 	 * Unless you know what you are doing, you will never need this.
80 	 *
81 	 * @return The list of supported server host key algorthims by this implementation.
82 	 */
getAvailableServerHostKeyAlgorithms()83 	public static synchronized String[] getAvailableServerHostKeyAlgorithms()
84 	{
85 		return KexManager.getDefaultServerHostkeyAlgorithmList();
86 	}
87 
88 	private AuthenticationManager am;
89 
90 	private boolean authenticated = false;
91 	private ChannelManager cm;
92 
93 	private CryptoWishList cryptoWishList = new CryptoWishList();
94 
95 	private DHGexParameters dhgexpara = new DHGexParameters();
96 
97 	private final String hostname;
98 
99 	private final int port;
100 
101 	private TransportManager tm;
102 
103 	private boolean tcpNoDelay = false;
104 
105 	private ProxyData proxyData = null;
106 
107 	private List<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
108 
109 	/**
110 	 * Prepares a fresh <code>Connection</code> object which can then be used
111 	 * to establish a connection to the specified SSH-2 server.
112 	 * <p>
113 	 * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
114 	 *
115 	 * @param hostname the hostname of the SSH-2 server.
116 	 */
Connection(String hostname)117 	public Connection(String hostname)
118 	{
119 		this(hostname, 22);
120 	}
121 
122 	/**
123 	 * Prepares a fresh <code>Connection</code> object which can then be used
124 	 * to establish a connection to the specified SSH-2 server.
125 	 *
126 	 * @param hostname
127 	 *            the host where we later want to connect to.
128 	 * @param port
129 	 *            port on the server, normally 22.
130 	 */
Connection(String hostname, int port)131 	public Connection(String hostname, int port)
132 	{
133 		this.hostname = hostname;
134 		this.port = port;
135 	}
136 
Connection(String hostname, int port, String identification)137 	public Connection(String hostname, int port, String identification)
138 	{
139 		this.hostname = hostname;
140 		this.port = port;
141         this.identification = identification;
142 	}
143 
144 	/**
145 	 * After a successful connect, one has to authenticate oneself. This method
146 	 * is based on DSA (it uses DSA to sign a challenge sent by the server).
147 	 * <p>
148 	 * If the authentication phase is complete, <code>true</code> will be
149 	 * returned. If the server does not accept the request (or if further
150 	 * authentication steps are needed), <code>false</code> is returned and
151 	 * one can retry either by using this or any other authentication method
152 	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
153 	 * the remaining possible methods).
154 	 *
155 	 * @param user
156 	 *            A <code>String</code> holding the username.
157 	 * @param pem
158 	 *            A <code>String</code> containing the DSA private key of the
159 	 *            user in OpenSSH key format (PEM, you can't miss the
160 	 *            "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
161 	 *            linefeeds.
162 	 * @param password
163 	 *            If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
164 	 *            must specify the password. Otherwise, this argument will be
165 	 *            ignored and can be set to <code>null</code>.
166 	 *
167 	 * @return whether the connection is now authenticated.
168 	 * @throws IOException
169 	 *
170 	 * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
171 	 * 		      methods, this method is just a wrapper for it and will
172 	 *            disappear in future builds.
173 	 *
174 	 */
authenticateWithDSA(String user, String pem, String password)175 	public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
176 	{
177 		if (tm == null)
178 			throw new IllegalStateException("Connection is not established!");
179 
180 		if (authenticated)
181 			throw new IllegalStateException("Connection is already authenticated!");
182 
183 		if (am == null)
184 			am = new AuthenticationManager(tm);
185 
186 		if (cm == null)
187 			cm = new ChannelManager(tm);
188 
189 		if (user == null)
190 			throw new IllegalArgumentException("user argument is null");
191 
192 		if (pem == null)
193 			throw new IllegalArgumentException("pem argument is null");
194 
195 		authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
196 
197 		return authenticated;
198 	}
199 
200 	/**
201 	 * A wrapper that calls {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
202 	 * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod list.
203 	 *
204 	 * @param user
205 	 *            A <code>String</code> holding the username.
206 	 * @param cb
207 	 *            An <code>InteractiveCallback</code> which will be used to
208 	 *            determine the responses to the questions asked by the server.
209 	 * @return whether the connection is now authenticated.
210 	 * @throws IOException
211 	 */
authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)212 	public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
213 			throws IOException
214 	{
215 		return authenticateWithKeyboardInteractive(user, null, cb);
216 	}
217 
218 	/**
219 	 * After a successful connect, one has to authenticate oneself. This method
220 	 * is based on "keyboard-interactive", specified in
221 	 * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
222 	 * callback object which will be feeded with challenges generated by the
223 	 * server. Answers are then sent back to the server. It is possible that the
224 	 * callback will be called several times during the invocation of this
225 	 * method (e.g., if the server replies to the callback's answer(s) with
226 	 * another challenge...)
227 	 * <p>
228 	 * If the authentication phase is complete, <code>true</code> will be
229 	 * returned. If the server does not accept the request (or if further
230 	 * authentication steps are needed), <code>false</code> is returned and
231 	 * one can retry either by using this or any other authentication method
232 	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
233 	 * the remaining possible methods).
234 	 * <p>
235 	 * Note: some SSH servers advertise "keyboard-interactive", however, any
236 	 * interactive request will be denied (without having sent any challenge to
237 	 * the client).
238 	 *
239 	 * @param user
240 	 *            A <code>String</code> holding the username.
241 	 * @param submethods
242 	 *            An array of submethod names, see
243 	 *            draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
244 	 *            to indicate an empty list.
245 	 * @param cb
246 	 *            An <code>InteractiveCallback</code> which will be used to
247 	 *            determine the responses to the questions asked by the server.
248 	 *
249 	 * @return whether the connection is now authenticated.
250 	 * @throws IOException
251 	 */
authenticateWithKeyboardInteractive(String user, String[] submethods, InteractiveCallback cb)252 	public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
253 			InteractiveCallback cb) throws IOException
254 	{
255 		if (cb == null)
256 			throw new IllegalArgumentException("Callback may not ne NULL!");
257 
258 		if (tm == null)
259 			throw new IllegalStateException("Connection is not established!");
260 
261 		if (authenticated)
262 			throw new IllegalStateException("Connection is already authenticated!");
263 
264 		if (am == null)
265 			am = new AuthenticationManager(tm);
266 
267 		if (cm == null)
268 			cm = new ChannelManager(tm);
269 
270 		if (user == null)
271 			throw new IllegalArgumentException("user argument is null");
272 
273 		authenticated = am.authenticateInteractive(user, submethods, cb);
274 
275 		return authenticated;
276 	}
277 
278 	/**
279 	 * After a successful connect, one has to authenticate oneself. This method
280 	 * sends username and password to the server.
281 	 * <p>
282 	 * If the authentication phase is complete, <code>true</code> will be
283 	 * returned. If the server does not accept the request (or if further
284 	 * authentication steps are needed), <code>false</code> is returned and
285 	 * one can retry either by using this or any other authentication method
286 	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
287 	 * the remaining possible methods).
288 	 * <p>
289 	 * Note: if this method fails, then please double-check that it is actually
290 	 * offered by the server (use {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
291 	 * <p>
292 	 * Often, password authentication is disabled, but users are not aware of it.
293 	 * Many servers only offer "publickey" and "keyboard-interactive". However,
294 	 * even though "keyboard-interactive" *feels* like password authentication
295 	 * (e.g., when using the putty or openssh clients) it is *not* the same mechanism.
296 	 *
297 	 * @param user
298 	 * @param password
299 	 * @return if the connection is now authenticated.
300 	 * @throws IOException
301 	 */
authenticateWithPassword(String user, String password)302 	public synchronized boolean authenticateWithPassword(String user, String password) throws IOException
303 	{
304 		if (tm == null)
305 			throw new IllegalStateException("Connection is not established!");
306 
307 		if (authenticated)
308 			throw new IllegalStateException("Connection is already authenticated!");
309 
310 		if (am == null)
311 			am = new AuthenticationManager(tm);
312 
313 		if (cm == null)
314 			cm = new ChannelManager(tm);
315 
316 		if (user == null)
317 			throw new IllegalArgumentException("user argument is null");
318 
319 		if (password == null)
320 			throw new IllegalArgumentException("password argument is null");
321 
322 		authenticated = am.authenticatePassword(user, password);
323 
324 		return authenticated;
325 	}
326 
327 	/**
328 	 * After a successful connect, one has to authenticate oneself.
329 	 * This method can be used to explicitly use the special "none"
330 	 * authentication method (where only a username has to be specified).
331 	 * <p>
332 	 * Note 1: The "none" method may always be tried by clients, however as by
333 	 * the specs, the server will not explicitly announce it. In other words,
334 	 * the "none" token will never show up in the list returned by
335 	 * {@link #getRemainingAuthMethods(String)}.
336 	 * <p>
337 	 * Note 2: no matter which one of the authenticateWithXXX() methods
338 	 * you call, the library will always issue exactly one initial "none"
339 	 * authentication request to retrieve the initially allowed list of
340 	 * authentication methods by the server. Please read RFC 4252 for the
341 	 * details.
342 	 * <p>
343 	 * If the authentication phase is complete, <code>true</code> will be
344 	 * returned. If further authentication steps are needed, <code>false</code>
345 	 * is returned and one can retry by any other authentication method
346 	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
347 	 * the remaining possible methods).
348 	 *
349 	 * @param user
350 	 * @return if the connection is now authenticated.
351 	 * @throws IOException
352 	 */
authenticateWithNone(String user)353 	public synchronized boolean authenticateWithNone(String user) throws IOException
354 	{
355 		if (tm == null)
356 			throw new IllegalStateException("Connection is not established!");
357 
358 		if (authenticated)
359 			throw new IllegalStateException("Connection is already authenticated!");
360 
361 		if (am == null)
362 			am = new AuthenticationManager(tm);
363 
364 		if (cm == null)
365 			cm = new ChannelManager(tm);
366 
367 		if (user == null)
368 			throw new IllegalArgumentException("user argument is null");
369 
370 		/* Trigger the sending of the PacketUserauthRequestNone packet */
371 		/* (if not already done)                                       */
372 
373 		authenticated = am.authenticateNone(user);
374 
375 		return authenticated;
376 	}
377 
378 	/**
379 	 * After a successful connect, one has to authenticate oneself.
380 	 * The authentication method "publickey" works by signing a challenge
381 	 * sent by the server. The signature is either DSA or RSA based - it
382 	 * just depends on the type of private key you specify, either a DSA
383 	 * or RSA private key in PEM format. And yes, this is may seem to be a
384 	 * little confusing, the method is called "publickey" in the SSH-2 protocol
385 	 * specification, however since we need to generate a signature, you
386 	 * actually have to supply a private key =).
387 	 * <p>
388 	 * The private key contained in the PEM file may also be encrypted ("Proc-Type: 4,ENCRYPTED").
389 	 * The library supports DES-CBC and DES-EDE3-CBC encryption, as well
390 	 * as the more exotic PEM encrpytions AES-128-CBC, AES-192-CBC and AES-256-CBC.
391 	 * <p>
392 	 * If the authentication phase is complete, <code>true</code> will be
393 	 * returned. If the server does not accept the request (or if further
394 	 * authentication steps are needed), <code>false</code> is returned and
395 	 * one can retry either by using this or any other authentication method
396 	 * (use the <code>getRemainingAuthMethods</code> method to get a list of
397 	 * the remaining possible methods).
398 	 * <p>
399 	 * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
400 	 * it is not in the expected format. You have to convert it to the OpenSSH
401 	 * key format by using the "puttygen" tool (can be downloaded from the Putty
402 	 * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
403 	 * functionality to get a proper PEM file.
404 	 *
405 	 * @param user
406 	 *            A <code>String</code> holding the username.
407 	 * @param pemPrivateKey
408 	 *            A <code>char[]</code> containing a DSA or RSA private key of the
409 	 *            user in OpenSSH key format (PEM, you can't miss the
410 	 *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
411 	 *            tag). The char array may contain linebreaks/linefeeds.
412 	 * @param password
413 	 *            If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then
414 	 *            you must specify a password. Otherwise, this argument will be ignored
415 	 *            and can be set to <code>null</code>.
416 	 *
417 	 * @return whether the connection is now authenticated.
418 	 * @throws IOException
419 	 */
authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)420 	public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
421 			throws IOException
422 	{
423 		if (tm == null)
424 			throw new IllegalStateException("Connection is not established!");
425 
426 		if (authenticated)
427 			throw new IllegalStateException("Connection is already authenticated!");
428 
429 		if (am == null)
430 			am = new AuthenticationManager(tm);
431 
432 		if (cm == null)
433 			cm = new ChannelManager(tm);
434 
435 		if (user == null)
436 			throw new IllegalArgumentException("user argument is null");
437 
438 		if (pemPrivateKey == null)
439 			throw new IllegalArgumentException("pemPrivateKey argument is null");
440 
441 		authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
442 
443 		return authenticated;
444 	}
445 
446 	/**
447 	 * A convenience wrapper function which reads in a private key (PEM format, either DSA or RSA)
448 	 * and then calls <code>authenticateWithPublicKey(String, char[], String)</code>.
449 	 * <p>
450 	 * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
451 	 * it is not in the expected format. You have to convert it to the OpenSSH
452 	 * key format by using the "puttygen" tool (can be downloaded from the Putty
453 	 * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
454 	 * functionality to get a proper PEM file.
455 	 *
456 	 * @param user
457 	 *            A <code>String</code> holding the username.
458 	 * @param pemFile
459 	 *            A <code>File</code> object pointing to a file containing a DSA or RSA
460 	 *            private key of the user in OpenSSH key format (PEM, you can't miss the
461 	 *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
462 	 *            tag).
463 	 * @param password
464 	 *            If the PEM file is encrypted then you must specify the password.
465 	 *            Otherwise, this argument will be ignored and can be set to <code>null</code>.
466 	 *
467 	 * @return whether the connection is now authenticated.
468 	 * @throws IOException
469 	 */
authenticateWithPublicKey(String user, File pemFile, String password)470 	public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
471 			throws IOException
472 	{
473 		if (pemFile == null)
474 			throw new IllegalArgumentException("pemFile argument is null");
475 
476 		char[] buff = new char[256];
477 
478 		CharArrayWriter cw = new CharArrayWriter();
479 
480 		FileReader fr = new FileReader(pemFile);
481 
482 		while (true)
483 		{
484 			int len = fr.read(buff);
485 			if (len < 0)
486 				break;
487 			cw.write(buff, 0, len);
488 		}
489 
490 		fr.close();
491 
492 		return authenticateWithPublicKey(user, cw.toCharArray(), password);
493 	}
494 
495 	/**
496 	 * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any time,
497 	 * but it is best to add connection monitors before invoking
498 	 * <code>connect()</code> to avoid glitches (e.g., you add a connection monitor after
499 	 * a successful connect(), but the connection has died in the mean time. Then,
500 	 * your connection monitor won't be notified.)
501 	 * <p>
502 	 * You can add as many monitors as you like.
503 	 *
504 	 * @see ConnectionMonitor
505 	 *
506 	 * @param cmon An object implementing the <code>ConnectionMonitor</code> interface.
507 	 */
addConnectionMonitor(ConnectionMonitor cmon)508 	public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
509 	{
510 		if (cmon == null)
511 			throw new IllegalArgumentException("cmon argument is null");
512 
513 		connectionMonitors.add(cmon);
514 
515 		if (tm != null)
516 			tm.setConnectionMonitors(connectionMonitors);
517 	}
518 
519 	/**
520 	 * Close the connection to the SSH-2 server. All assigned sessions will be
521 	 * closed, too. Can be called at any time. Don't forget to call this once
522 	 * you don't need a connection anymore - otherwise the receiver thread may
523 	 * run forever.
524 	 */
close()525 	public synchronized void close()
526 	{
527 		Throwable t = new Throwable("Closed due to user request.");
528 		close(t, false);
529 	}
530 
close(Throwable t, boolean hard)531     public void close(Throwable t, boolean hard)
532 	{
533 		if (cm != null)
534 			cm.closeAllChannels();
535 
536 		if (tm != null)
537 		{
538 			tm.close(t, hard == false);
539 			tm = null;
540 		}
541 		am = null;
542 		cm = null;
543 		authenticated = false;
544 	}
545 
546 	/**
547 	 * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
548 	 *
549 	 * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
550 	 * @throws IOException
551 	 */
connect()552 	public synchronized ConnectionInfo connect() throws IOException
553 	{
554 		return connect(null, 0, 0);
555 	}
556 
557 	/**
558 	 * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
559 	 *
560 	 * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
561 	 * @throws IOException
562 	 */
connect(ServerHostKeyVerifier verifier)563 	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
564 	{
565 		return connect(verifier, 0, 0);
566 	}
567 
568 	/**
569 	 * Connect to the SSH-2 server and, as soon as the server has presented its
570 	 * host key, use the {@link ServerHostKeyVerifier#verifyServerHostKey(String,
571 	 * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}
572 	 * method of the <code>verifier</code> to ask for permission to proceed.
573 	 * If <code>verifier</code> is <code>null</code>, then any host key will be
574 	 * accepted - this is NOT recommended, since it makes man-in-the-middle attackes
575 	 * VERY easy (somebody could put a proxy SSH server between you and the real server).
576 	 * <p>
577 	 * Note: The verifier will be called before doing any crypto calculations
578 	 * (i.e., diffie-hellman). Therefore, if you don't like the presented host key then
579 	 * no CPU cycles are wasted (and the evil server has less information about us).
580 	 * <p>
581 	 * However, it is still possible that the server presented a fake host key: the server
582 	 * cheated (typically a sign for a man-in-the-middle attack) and is not able to generate
583 	 * a signature that matches its host key. Don't worry, the library will detect such
584 	 * a scenario later when checking the signature (the signature cannot be checked before
585 	 * having completed the diffie-hellman exchange).
586 	 * <p>
587 	 * Note 2: The  {@link ServerHostKeyVerifier#verifyServerHostKey(String,
588 	 * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method
589 	 * will *NOT* be called from the current thread, the call is being made from a
590 	 * background thread (there is a background dispatcher thread for every
591 	 * established connection).
592 	 * <p>
593 	 * Note 3: This method will block as long as the key exchange of the underlying connection
594 	 * has not been completed (and you have not specified any timeouts).
595 	 * <p>
596 	 * Note 4: If you want to re-use a connection object that was successfully connected,
597 	 * then you must call the {@link #close()} method before invoking <code>connect()</code> again.
598 	 *
599 	 * @param verifier
600 	 *            An object that implements the
601 	 *            {@link ServerHostKeyVerifier} interface. Pass <code>null</code>
602 	 *            to accept any server host key - NOT recommended.
603 	 *
604 	 * @param connectTimeout
605 	 *            Connect the underlying TCP socket to the server with the given timeout
606 	 *            value (non-negative, in milliseconds). Zero means no timeout. If a proxy is being
607 	 *            used (see {@link #setProxyData(ProxyData)}), then this timeout is used for the
608 	 *            connection establishment to the proxy.
609 	 *
610 	 * @param kexTimeout
611 	 *            Timeout for complete connection establishment (non-negative,
612 	 *            in milliseconds). Zero means no timeout. The timeout counts from the
613 	 *            moment you invoke the connect() method and is cancelled as soon as the
614 	 *            first key-exchange round has finished. It is possible that
615 	 *            the timeout event will be fired during the invocation of the
616 	 *            <code>verifier</code> callback, but it will only have an effect after
617 	 *            the <code>verifier</code> returns.
618 	 *
619 	 * @return A {@link ConnectionInfo} object containing the details of
620 	 *            the established connection.
621 	 *
622 	 * @throws IOException
623 	 *            If any problem occurs, e.g., the server's host key is not
624 	 *            accepted by the <code>verifier</code> or there is problem during
625 	 *            the initial crypto setup (e.g., the signature sent by the server is wrong).
626 	 *            <p>
627 	 *            In case of a timeout (either connectTimeout or kexTimeout)
628 	 *            a SocketTimeoutException is thrown.
629 	 *            <p>
630 	 *            An exception may also be thrown if the connection was already successfully
631 	 *            connected (no matter if the connection broke in the mean time) and you invoke
632 	 *            <code>connect()</code> again without having called {@link #close()} first.
633 	 *            <p>
634 	 *            If a HTTP proxy is being used and the proxy refuses the connection,
635 	 *            then a {@link HTTPProxyException} may be thrown, which
636 	 *            contains the details returned by the proxy. If the proxy is buggy and does
637 	 *            not return a proper HTTP response, then a normal IOException is thrown instead.
638 	 */
connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)639 	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
640 			throws IOException
641 	{
642 		final class TimeoutState
643 		{
644 			boolean isCancelled = false;
645 			boolean timeoutSocketClosed = false;
646 		}
647 
648 		if (tm != null)
649 			throw new IOException("Connection to " + hostname + " is already in connected state!");
650 
651 		if (connectTimeout < 0)
652 			throw new IllegalArgumentException("connectTimeout must be non-negative!");
653 
654 		if (kexTimeout < 0)
655 			throw new IllegalArgumentException("kexTimeout must be non-negative!");
656 
657 		final TimeoutState state = new TimeoutState();
658 
659 		tm = new TransportManager(hostname, port);
660         tm.setSoTimeout(connectTimeout);
661 		tm.setConnectionMonitors(connectionMonitors);
662 
663 		/* Make sure that the runnable below will observe the new value of "tm"
664 		 * and "state" (the runnable will be executed in a different thread, which
665 		 * may be already running, that is why we need a memory barrier here).
666 		 * See also the comment in Channel.java if you
667 		 * are interested in the details.
668 		 *
669 		 * OKOK, this is paranoid since adding the runnable to the todo list
670 		 * of the TimeoutService will ensure that all writes have been flushed
671 		 * before the Runnable reads anything
672 		 * (there is a synchronized block in TimeoutService.addTimeoutHandler).
673 		 */
674 
675 		synchronized (tm)
676 		{
677 			/* We could actually synchronize on anything. */
678 		}
679 
680 		try
681 		{
682 			TimeoutToken token = null;
683 
684 			if (kexTimeout > 0)
685 			{
686 				final Runnable timeoutHandler = new Runnable()
687 				{
688 					public void run()
689 					{
690 						synchronized (state)
691 						{
692 							if (state.isCancelled)
693 								return;
694 							state.timeoutSocketClosed = true;
695 							tm.close(new SocketTimeoutException("The connect timeout expired"), false);
696 						}
697 					}
698 				};
699 
700 				long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
701 
702 				token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
703 			}
704 
705 			try
706 			{
707 				tm.initialize(identification, cryptoWishList, verifier, dhgexpara, connectTimeout,
708                         getOrCreateSecureRND(), proxyData);
709 			}
710 			catch (SocketTimeoutException se)
711 			{
712 				throw (SocketTimeoutException) new SocketTimeoutException(
713 						"The connect() operation on the socket timed out.").initCause(se);
714 			}
715 
716 			tm.setTcpNoDelay(tcpNoDelay);
717 
718 			/* Wait until first KEX has finished */
719 
720 			ConnectionInfo ci = tm.getConnectionInfo(1);
721 
722 			/* Now try to cancel the timeout, if needed */
723 
724 			if (token != null)
725 			{
726 				TimeoutService.cancelTimeoutHandler(token);
727 
728 				/* Were we too late? */
729 
730 				synchronized (state)
731 				{
732 					if (state.timeoutSocketClosed)
733 						throw new IOException("This exception will be replaced by the one below =)");
734 					/* Just in case the "cancelTimeoutHandler" invocation came just a little bit
735 					 * too late but the handler did not enter the semaphore yet - we can
736 					 * still stop it.
737 					 */
738 					state.isCancelled = true;
739 				}
740 			}
741 
742 			return ci;
743 		}
744 		catch (SocketTimeoutException ste)
745 		{
746 			throw ste;
747 		}
748 		catch (IOException e1)
749 		{
750 			/* This will also invoke any registered connection monitors */
751 			close(new Throwable("There was a problem during connect."), false);
752 
753 			synchronized (state)
754 			{
755 				/* Show a clean exception, not something like "the socket is closed!?!" */
756 				if (state.timeoutSocketClosed)
757 					throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
758 			}
759 
760 			/* Do not wrap a HTTPProxyException */
761 			if (e1 instanceof HTTPProxyException)
762 				throw e1;
763 
764 			throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
765 					.initCause(e1);
766 		}
767 	}
768 
769 	/**
770 	 * Creates a new {@link LocalPortForwarder}.
771 	 * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
772 	 * port via the secure tunnel to another host (which may or may not be
773 	 * identical to the remote SSH-2 server).
774 	 * <p>
775 	 * This method must only be called after one has passed successfully the authentication step.
776 	 * There is no limit on the number of concurrent forwardings.
777 	 *
778 	 * @param local_port the local port the LocalPortForwarder shall bind to.
779 	 * @param host_to_connect target address (IP or hostname)
780 	 * @param port_to_connect target port
781 	 * @return A {@link LocalPortForwarder} object.
782 	 * @throws IOException
783 	 */
createLocalPortForwarder(int local_port, String host_to_connect, int port_to_connect)784 	public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
785 			int port_to_connect) throws IOException
786 	{
787 		if (tm == null)
788 			throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
789 
790 		if (!authenticated)
791 			throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
792 
793 		return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
794 	}
795 
796 	/**
797 	 * Creates a new {@link LocalPortForwarder}.
798 	 * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
799 	 * port via the secure tunnel to another host (which may or may not be
800 	 * identical to the remote SSH-2 server).
801 	 * <p>
802 	 * This method must only be called after one has passed successfully the authentication step.
803 	 * There is no limit on the number of concurrent forwardings.
804 	 *
805 	 * @param addr specifies the InetSocketAddress where the local socket shall be bound to.
806 	 * @param host_to_connect target address (IP or hostname)
807 	 * @param port_to_connect target port
808 	 * @return A {@link LocalPortForwarder} object.
809 	 * @throws IOException
810 	 */
createLocalPortForwarder(InetSocketAddress addr, String host_to_connect, int port_to_connect)811 	public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
812 			int port_to_connect) throws IOException
813 	{
814 		if (tm == null)
815 			throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
816 
817 		if (!authenticated)
818 			throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
819 
820 		return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
821 	}
822 
823 	/**
824 	 * Creates a new {@link LocalStreamForwarder}.
825 	 * A <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
826 	 * that is being forwarded via the secure tunnel into a TCP/IP connection to another host
827 	 * (which may or may not be identical to the remote SSH-2 server).
828 	 *
829 	 * @param host_to_connect
830 	 * @param port_to_connect
831 	 * @return A {@link LocalStreamForwarder} object.
832 	 * @throws IOException
833 	 */
createLocalStreamForwarder(String host_to_connect, int port_to_connect)834 	public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
835 			throws IOException
836 	{
837 		if (tm == null)
838 			throw new IllegalStateException("Cannot forward, you need to establish a connection first.");
839 
840 		if (!authenticated)
841 			throw new IllegalStateException("Cannot forward, connection is not authenticated.");
842 
843 		return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
844 	}
845 
846 	/**
847 	 * Create a very basic {@link SCPClient} that can be used to copy
848 	 * files from/to the SSH-2 server.
849 	 * <p>
850 	 * Works only after one has passed successfully the authentication step.
851 	 * There is no limit on the number of concurrent SCP clients.
852 	 * <p>
853 	 * Note: This factory method will probably disappear in the future.
854 	 *
855 	 * @return A {@link SCPClient} object.
856 	 * @throws IOException
857 	 */
createSCPClient()858 	public synchronized SCPClient createSCPClient() throws IOException
859 	{
860 		if (tm == null)
861 			throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");
862 
863 		if (!authenticated)
864 			throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");
865 
866 		return new SCPClient(this);
867 	}
868 
869 	/**
870 	 * Force an asynchronous key re-exchange (the call does not block). The
871 	 * latest values set for MAC, Cipher and DH group exchange parameters will
872 	 * be used. If a key exchange is currently in progress, then this method has
873 	 * the only effect that the so far specified parameters will be used for the
874 	 * next (server driven) key exchange.
875 	 * <p>
876 	 * Note: This implementation will never start a key exchange (other than the initial one)
877 	 * unless you or the SSH-2 server ask for it.
878 	 *
879 	 * @throws IOException
880 	 *             In case of any failure behind the scenes.
881 	 */
forceKeyExchange()882 	public synchronized void forceKeyExchange() throws IOException
883 	{
884 		if (tm == null)
885 			throw new IllegalStateException("You need to establish a connection first.");
886 
887 		tm.forceKeyExchange(cryptoWishList, dhgexpara);
888 	}
889 
890 	/**
891 	 * Returns the hostname that was passed to the constructor.
892 	 *
893 	 * @return the hostname
894 	 */
getHostname()895 	public synchronized String getHostname()
896 	{
897 		return hostname;
898 	}
899 
900 	/**
901 	 * Returns the port that was passed to the constructor.
902 	 *
903 	 * @return the TCP port
904 	 */
getPort()905 	public synchronized int getPort()
906 	{
907 		return port;
908 	}
909 
910 	/**
911 	 * Returns a {@link ConnectionInfo} object containing the details of
912 	 * the connection. Can be called as soon as the connection has been
913 	 * established (successfully connected).
914 	 *
915 	 * @return A {@link ConnectionInfo} object.
916 	 * @throws IOException
917 	 *             In case of any failure behind the scenes.
918 	 */
getConnectionInfo()919 	public synchronized ConnectionInfo getConnectionInfo() throws IOException
920 	{
921 		if (tm == null)
922 			throw new IllegalStateException(
923 					"Cannot get details of connection, you need to establish a connection first.");
924 		return tm.getConnectionInfo(1);
925 	}
926 
927 	/**
928 	 * After a successful connect, one has to authenticate oneself. This method
929 	 * can be used to tell which authentication methods are supported by the
930 	 * server at a certain stage of the authentication process (for the given
931 	 * username).
932 	 * <p>
933 	 * Note 1: the username will only be used if no authentication step was done
934 	 * so far (it will be used to ask the server for a list of possible
935 	 * authentication methods by sending the initial "none" request). Otherwise,
936 	 * this method ignores the user name and returns a cached method list
937 	 * (which is based on the information contained in the last negative server response).
938 	 * <p>
939 	 * Note 2: the server may return method names that are not supported by this
940 	 * implementation.
941 	 * <p>
942 	 * After a successful authentication, this method must not be called
943 	 * anymore.
944 	 *
945 	 * @param user
946 	 *            A <code>String</code> holding the username.
947 	 *
948 	 * @return a (possibly emtpy) array holding authentication method names.
949 	 * @throws IOException
950 	 */
getRemainingAuthMethods(String user)951 	public synchronized String[] getRemainingAuthMethods(String user) throws IOException
952 	{
953 		if (user == null)
954 			throw new IllegalArgumentException("user argument may not be NULL!");
955 
956 		if (tm == null)
957 			throw new IllegalStateException("Connection is not established!");
958 
959 		if (authenticated)
960 			throw new IllegalStateException("Connection is already authenticated!");
961 
962 		if (am == null)
963 			am = new AuthenticationManager(tm);
964 
965 		if (cm == null)
966 			cm = new ChannelManager(tm);
967 
968 		return am.getRemainingMethods(user);
969 	}
970 
971 	/**
972 	 * Determines if the authentication phase is complete. Can be called at any
973 	 * time.
974 	 *
975 	 * @return <code>true</code> if no further authentication steps are
976 	 *         needed.
977 	 */
isAuthenticationComplete()978 	public synchronized boolean isAuthenticationComplete()
979 	{
980 		return authenticated;
981 	}
982 
983 	/**
984 	 * Returns true if there was at least one failed authentication request and
985 	 * the last failed authentication request was marked with "partial success"
986 	 * by the server. This is only needed in the rare case of SSH-2 server setups
987 	 * that cannot be satisfied with a single successful authentication request
988 	 * (i.e., multiple authentication steps are needed.)
989 	 * <p>
990 	 * If you are interested in the details, then have a look at RFC4252.
991 	 *
992 	 * @return if the there was a failed authentication step and the last one
993 	 *         was marked as a "partial success".
994 	 */
isAuthenticationPartialSuccess()995 	public synchronized boolean isAuthenticationPartialSuccess()
996 	{
997 		if (am == null)
998 			return false;
999 
1000 		return am.getPartialSuccess();
1001 	}
1002 
1003 	/**
1004 	 * Checks if a specified authentication method is available. This method is
1005 	 * actually just a wrapper for {@link #getRemainingAuthMethods(String)
1006 	 * getRemainingAuthMethods()}.
1007 	 *
1008 	 * @param user
1009 	 *            A <code>String</code> holding the username.
1010 	 * @param method
1011 	 *            An authentication method name (e.g., "publickey", "password",
1012 	 *            "keyboard-interactive") as specified by the SSH-2 standard.
1013 	 * @return if the specified authentication method is currently available.
1014 	 * @throws IOException
1015 	 */
isAuthMethodAvailable(String user, String method)1016 	public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException
1017 	{
1018 		if (method == null)
1019 			throw new IllegalArgumentException("method argument may not be NULL!");
1020 
1021 		String methods[] = getRemainingAuthMethods(user);
1022 
1023 		for (int i = 0; i < methods.length; i++)
1024 		{
1025 			if (methods[i].compareTo(method) == 0)
1026 				return true;
1027 		}
1028 
1029 		return false;
1030 	}
1031 
getOrCreateSecureRND()1032 	private SecureRandom getOrCreateSecureRND()
1033 	{
1034 		if (generator == null)
1035 			generator = new SecureRandom();
1036 
1037 		return generator;
1038 	}
1039 
1040 	/**
1041 	 * Open a new {@link Session} on this connection. Works only after one has passed
1042 	 * successfully the authentication step. There is no limit on the number of
1043 	 * concurrent sessions.
1044 	 *
1045 	 * @return A {@link Session} object.
1046 	 * @throws IOException
1047 	 */
openSession()1048 	public synchronized Session openSession() throws IOException
1049 	{
1050 		if (tm == null)
1051 			throw new IllegalStateException("Cannot open session, you need to establish a connection first.");
1052 
1053 		if (!authenticated)
1054 			throw new IllegalStateException("Cannot open session, connection is not authenticated.");
1055 
1056 		return new Session(cm, getOrCreateSecureRND());
1057 	}
1058 
1059 	/**
1060 	 * Send an SSH_MSG_IGNORE packet. This method will generate a random data attribute
1061 	 * (length between 0 (invlusive) and 16 (exclusive) bytes, contents are random bytes).
1062 	 * <p>
1063 	 * This method must only be called once the connection is established.
1064 	 *
1065 	 * @throws IOException
1066 	 */
sendIgnorePacket()1067 	public synchronized void sendIgnorePacket() throws IOException
1068 	{
1069 		SecureRandom rnd = getOrCreateSecureRND();
1070 
1071 		byte[] data = new byte[rnd.nextInt(16)];
1072 		rnd.nextBytes(data);
1073 
1074 		sendIgnorePacket(data);
1075 	}
1076 
1077 	/**
1078 	 * Send an SSH_MSG_IGNORE packet with the given data attribute.
1079 	 * <p>
1080 	 * This method must only be called once the connection is established.
1081 	 *
1082 	 * @throws IOException
1083 	 */
sendIgnorePacket(byte[] data)1084 	public synchronized void sendIgnorePacket(byte[] data) throws IOException
1085 	{
1086 		if (data == null)
1087 			throw new IllegalArgumentException("data argument must not be null.");
1088 
1089 		if (tm == null)
1090 			throw new IllegalStateException(
1091 					"Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first.");
1092 
1093 		PacketIgnore pi = new PacketIgnore();
1094 		pi.setData(data);
1095 
1096 		tm.sendMessage(pi.getPayload());
1097 	}
1098 
1099 	/**
1100 	 * Removes duplicates from a String array, keeps only first occurence
1101 	 * of each element. Does not destroy order of elements; can handle nulls.
1102 	 * Uses a very efficient O(N^2) algorithm =)
1103 	 *
1104 	 * @param list a String array.
1105 	 * @return a cleaned String array.
1106 	 */
removeDuplicates(String[] list)1107 	private String[] removeDuplicates(String[] list)
1108 	{
1109 		if ((list == null) || (list.length < 2))
1110 			return list;
1111 
1112 		String[] list2 = new String[list.length];
1113 
1114 		int count = 0;
1115 
1116 		for (int i = 0; i < list.length; i++)
1117 		{
1118 			boolean duplicate = false;
1119 
1120 			String element = list[i];
1121 
1122 			for (int j = 0; j < count; j++)
1123 			{
1124 				if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j]))))
1125 				{
1126 					duplicate = true;
1127 					break;
1128 				}
1129 			}
1130 
1131 			if (duplicate)
1132 				continue;
1133 
1134 			list2[count++] = list[i];
1135 		}
1136 
1137 		if (count == list2.length)
1138 			return list2;
1139 
1140 		String[] tmp = new String[count];
1141 		System.arraycopy(list2, 0, tmp, 0, count);
1142 
1143 		return tmp;
1144 	}
1145 
1146 	/**
1147 	 * Unless you know what you are doing, you will never need this.
1148 	 *
1149 	 * @param ciphers
1150 	 */
setClient2ServerCiphers(String[] ciphers)1151 	public synchronized void setClient2ServerCiphers(String[] ciphers)
1152 	{
1153 		if ((ciphers == null) || (ciphers.length == 0))
1154 			throw new IllegalArgumentException();
1155 		ciphers = removeDuplicates(ciphers);
1156 		BlockCipherFactory.checkCipherList(ciphers);
1157 		cryptoWishList.c2s_enc_algos = ciphers;
1158 	}
1159 
1160 	/**
1161 	 * Unless you know what you are doing, you will never need this.
1162 	 *
1163 	 * @param macs
1164 	 */
setClient2ServerMACs(String[] macs)1165 	public synchronized void setClient2ServerMACs(String[] macs)
1166 	{
1167 		if ((macs == null) || (macs.length == 0))
1168 			throw new IllegalArgumentException();
1169 		macs = removeDuplicates(macs);
1170 		MAC.checkMacList(macs);
1171 		cryptoWishList.c2s_mac_algos = macs;
1172 	}
1173 
1174 	/**
1175 	 * Sets the parameters for the diffie-hellman group exchange. Unless you
1176 	 * know what you are doing, you will never need this. Default values are
1177 	 * defined in the {@link DHGexParameters} class.
1178 	 *
1179 	 * @param dgp {@link DHGexParameters}, non null.
1180 	 *
1181 	 */
setDHGexParameters(DHGexParameters dgp)1182 	public synchronized void setDHGexParameters(DHGexParameters dgp)
1183 	{
1184 		if (dgp == null)
1185 			throw new IllegalArgumentException();
1186 
1187 		dhgexpara = dgp;
1188 	}
1189 
1190 	/**
1191 	 * Unless you know what you are doing, you will never need this.
1192 	 *
1193 	 * @param ciphers
1194 	 */
setServer2ClientCiphers(String[] ciphers)1195 	public synchronized void setServer2ClientCiphers(String[] ciphers)
1196 	{
1197 		if ((ciphers == null) || (ciphers.length == 0))
1198 			throw new IllegalArgumentException();
1199 		ciphers = removeDuplicates(ciphers);
1200 		BlockCipherFactory.checkCipherList(ciphers);
1201 		cryptoWishList.s2c_enc_algos = ciphers;
1202 	}
1203 
1204 	/**
1205 	 * Unless you know what you are doing, you will never need this.
1206 	 *
1207 	 * @param macs
1208 	 */
setServer2ClientMACs(String[] macs)1209 	public synchronized void setServer2ClientMACs(String[] macs)
1210 	{
1211 		if ((macs == null) || (macs.length == 0))
1212 			throw new IllegalArgumentException();
1213 
1214 		macs = removeDuplicates(macs);
1215 		MAC.checkMacList(macs);
1216 		cryptoWishList.s2c_mac_algos = macs;
1217 	}
1218 
1219 	/**
1220 	 * Define the set of allowed server host key algorithms to be used for
1221 	 * the following key exchange operations.
1222 	 * <p>
1223 	 * Unless you know what you are doing, you will never need this.
1224 	 *
1225 	 * @param algos An array of allowed server host key algorithms.
1226 	 * 	SSH-2 defines <code>ssh-dss</code> and <code>ssh-rsa</code>.
1227 	 * 	The entries of the array must be ordered after preference, i.e.,
1228 	 *  the entry at index 0 is the most preferred one. You must specify
1229 	 *  at least one entry.
1230 	 */
setServerHostKeyAlgorithms(String[] algos)1231 	public synchronized void setServerHostKeyAlgorithms(String[] algos)
1232 	{
1233 		if ((algos == null) || (algos.length == 0))
1234 			throw new IllegalArgumentException();
1235 
1236 		algos = removeDuplicates(algos);
1237 		KexManager.checkServerHostkeyAlgorithmsList(algos);
1238 		cryptoWishList.serverHostKeyAlgorithms = algos;
1239 	}
1240 
1241 	/**
1242 	 * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
1243 	 * <p>
1244 	 * Can be called at any time. If the connection has not yet been established
1245 	 * then the passed value will be stored and set after the socket has been set up.
1246 	 * The default value that will be used is <code>false</code>.
1247 	 *
1248 	 * @param enable the argument passed to the <code>Socket.setTCPNoDelay()</code> method.
1249 	 * @throws IOException
1250 	 */
setTCPNoDelay(boolean enable)1251 	public synchronized void setTCPNoDelay(boolean enable) throws IOException
1252 	{
1253 		tcpNoDelay = enable;
1254 
1255 		if (tm != null)
1256 			tm.setTcpNoDelay(enable);
1257 	}
1258 
1259 	/**
1260 	 * Used to tell the library that the connection shall be established through a proxy server.
1261 	 * It only makes sense to call this method before calling the {@link #connect() connect()}
1262 	 * method.
1263 	 * <p>
1264 	 * At the moment, only HTTP proxies are supported.
1265 	 * <p>
1266 	 * Note: This method can be called any number of times. The {@link #connect() connect()}
1267 	 * method will use the value set in the last preceding invocation of this method.
1268 	 *
1269 	 * @see HTTPProxyData
1270 	 *
1271 	 * @param proxyData Connection information about the proxy. If <code>null</code>, then
1272 	 *                  no proxy will be used (non surprisingly, this is also the default).
1273 	 */
setProxyData(ProxyData proxyData)1274 	public synchronized void setProxyData(ProxyData proxyData)
1275 	{
1276 		this.proxyData = proxyData;
1277 	}
1278 
1279 	/**
1280 	 * Request a remote port forwarding.
1281 	 * If successful, then forwarded connections will be redirected to the given target address.
1282 	 * You can cancle a requested remote port forwarding by calling
1283 	 * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
1284 	 * <p>
1285 	 * A call of this method will block until the peer either agreed or disagreed to your request-
1286 	 * <p>
1287 	 * Note 1: this method typically fails if you
1288 	 * <ul>
1289 	 * <li>pass a port number for which the used remote user has not enough permissions (i.e., port
1290 	 * &lt; 1024)</li>
1291 	 * <li>or pass a port number that is already in use on the remote server</li>
1292 	 * <li>or if remote port forwarding is disabled on the server.</li>
1293 	 * </ul>
1294 	 * <p>
1295 	 * Note 2: (from the openssh man page): By default, the listening socket on the server will be
1296 	 * bound to the loopback interface only. This may be overriden by specifying a bind address.
1297 	 * Specifying a remote bind address will only succeed if the server's <b>GatewayPorts</b> option
1298 	 * is enabled (see sshd_config(5)).
1299 	 *
1300 	 * @param bindAddress address to bind to on the server:
1301 	 *                    <ul>
1302 	 *                    <li>"" means that connections are to be accepted on all protocol families
1303 	 *                    supported by the SSH implementation</li>
1304 	 *                    <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
1305 	 *                    <li>"::" means to listen on all IPv6 addresses</li>
1306 	 *                    <li>"localhost" means to listen on all protocol families supported by the SSH
1307 	 *                    implementation on loopback addresses only, [RFC3330] and RFC3513]</li>
1308 	 *                    <li>"127.0.0.1" and "::1" indicate listening on the loopback interfaces for
1309 	 *                    IPv4 and IPv6 respectively</li>
1310 	 *                    </ul>
1311 	 * @param bindPort port number to bind on the server (must be &gt; 0)
1312 	 * @param targetAddress the target address (IP or hostname)
1313 	 * @param targetPort the target port
1314 	 * @throws IOException
1315 	 */
requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress, int targetPort)1316 	public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
1317 			int targetPort) throws IOException
1318 	{
1319 		if (tm == null)
1320 			throw new IllegalStateException("You need to establish a connection first.");
1321 
1322 		if (!authenticated)
1323 			throw new IllegalStateException("The connection is not authenticated.");
1324 
1325 		if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
1326 			throw new IllegalArgumentException();
1327 
1328 		cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
1329 	}
1330 
1331 	/**
1332 	 * Cancel an earlier requested remote port forwarding.
1333 	 * Currently active forwardings will not be affected (e.g., disrupted).
1334 	 * Note that further connection forwarding requests may be received until
1335 	 * this method has returned.
1336 	 *
1337 	 * @param bindPort the allocated port number on the server
1338 	 * @throws IOException if the remote side refuses the cancel request or another low
1339 	 *         level error occurs (e.g., the underlying connection is closed)
1340 	 */
cancelRemotePortForwarding(int bindPort)1341 	public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
1342 	{
1343 		if (tm == null)
1344 			throw new IllegalStateException("You need to establish a connection first.");
1345 
1346 		if (!authenticated)
1347 			throw new IllegalStateException("The connection is not authenticated.");
1348 
1349 		cm.requestCancelGlobalForward(bindPort);
1350 	}
1351 
1352 	/**
1353 	 * Provide your own instance of SecureRandom. Can be used, e.g., if you
1354 	 * want to seed the used SecureRandom generator manually.
1355 	 * <p>
1356 	 * The SecureRandom instance is used during key exchanges, public key authentication,
1357 	 * x11 cookie generation and the like.
1358 	 *
1359 	 * @param rnd a SecureRandom instance
1360 	 */
setSecureRandom(SecureRandom rnd)1361 	public synchronized void setSecureRandom(SecureRandom rnd)
1362 	{
1363 		if (rnd == null)
1364 			throw new IllegalArgumentException();
1365 
1366 		this.generator = rnd;
1367 	}
1368 }
1369