comparison src/ch/ethz/ssh2/Connection.java @ 342:175c7d68f3c4

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