comparison src/ch/ethz/ssh2/ServerConnection.java @ 308:42b15aaa7ac7 ganymed

merge
author Carl Byington <carl@five-ten-sg.com>
date Wed, 30 Jul 2014 14:21:50 -0700
parents 071eccdff8ea
children c19b24adf6c9
comparison
equal deleted inserted replaced
306:90e47d99ea54 308:42b15aaa7ac7
27 * A server-side SSH-2 connection. 27 * A server-side SSH-2 connection.
28 * 28 *
29 * @author Christian 29 * @author Christian
30 * 30 *
31 */ 31 */
32 public class ServerConnection 32 public class ServerConnection {
33 { 33 /**
34 /** 34 * The softwareversion presented to the SSH-2 client.
35 * The softwareversion presented to the SSH-2 client. 35 */
36 */ 36 private String softwareversion = String.format("Ganymed_SSHD_%s", Version.getSpecification());
37 private String softwareversion = String.format("Ganymed_SSHD_%s", Version.getSpecification()); 37
38 38 private final ServerConnectionState state = new ServerConnectionState(this);
39 private final ServerConnectionState state = new ServerConnectionState(this); 39
40 40 /**
41 /** 41 * Creates a new <code>ServerConnection</code> that will communicate
42 * Creates a new <code>ServerConnection</code> that will communicate 42 * with the client over the given <code>Socket</code>.
43 * with the client over the given <code>Socket</code>. 43 * <p>
44 * <p> 44 * Note: you need to call {@link #connect()} or {@link #connect(int)} to
45 * Note: you need to call {@link #connect()} or {@link #connect(int)} to 45 * perform the initial handshake and establish the encrypted communication.
46 * perform the initial handshake and establish the encrypted communication. 46 *
47 * 47 * @see #connect(int)
48 * @see #connect(int) 48 *
49 * 49 * @param s The socket
50 * @param s The socket 50 */
51 */ 51 public ServerConnection(Socket s) {
52 public ServerConnection(Socket s) 52 this(s, null, null, null);
53 { 53 }
54 this(s, null, null, null);
55 }
56 54
57 public ServerConnection(Socket s, String softwareversion) { 55 public ServerConnection(Socket s, String softwareversion) {
58 this(s, null, null, null); 56 this(s, null, null, null);
59 this.softwareversion = softwareversion; 57 this.softwareversion = softwareversion;
60 } 58 }
61 59
62 /** 60 /**
63 * Creates a new <code>ServerConnection</code> that will communicate 61 * Creates a new <code>ServerConnection</code> that will communicate
64 * with the client over the given <code>Socket</code>. 62 * with the client over the given <code>Socket</code>.
65 * <p> 63 * <p>
66 * Note: you need to call {@link #connect()} or {@link #connect(int)} to 64 * Note: you need to call {@link #connect()} or {@link #connect(int)} to
67 * perform the initial handshake and establish the encrypted communication. 65 * perform the initial handshake and establish the encrypted communication.
68 * <p> 66 * <p>
69 * Please read the javadoc for the {@link #connect(int)} method. 67 * Please read the javadoc for the {@link #connect(int)} method.
70 * 68 *
71 * @see #connect(int) 69 * @see #connect(int)
72 * 70 *
73 * @param s The socket 71 * @param s The socket
74 * @param dsa_key The DSA hostkey, may be <code>NULL</code> 72 * @param dsa_key The DSA hostkey, may be <code>NULL</code>
75 * @param rsa_key The RSA hostkey, may be <code>NULL</code> 73 * @param rsa_key The RSA hostkey, may be <code>NULL</code>
76 * @param ec_key The EC hostkey, may be <code>NULL</code> 74 * @param ec_key The EC hostkey, may be <code>NULL</code>
77 */ 75 */
78 public ServerConnection(Socket s, KeyPair dsa_key, KeyPair rsa_key, KeyPair ec_key) 76 public ServerConnection(Socket s, KeyPair dsa_key, KeyPair rsa_key, KeyPair ec_key) {
79 { 77 state.s = s;
80 state.s = s; 78 state.softwareversion = softwareversion;
81 state.softwareversion = softwareversion; 79 state.next_dsa_key = dsa_key;
82 state.next_dsa_key = dsa_key; 80 state.next_rsa_key = rsa_key;
83 state.next_rsa_key = rsa_key; 81 state.next_ec_key = ec_key;
84 state.next_ec_key = ec_key; 82 fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
85 fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key); 83 }
86 } 84
87 85 /**
88 /** 86 * Establish the connection and block until the first handshake has completed.
89 * Establish the connection and block until the first handshake has completed. 87 * <p>
90 * <p> 88 * Note: this is a wrapper that calls <code>connect(0)</code> (i.e., connect with no timeout).
91 * Note: this is a wrapper that calls <code>connect(0)</code> (i.e., connect with no timeout). 89 * <p>
92 * <p> 90 * Please read the javadoc for the {@link #connect(int)} method.
93 * Please read the javadoc for the {@link #connect(int)} method. 91 *
94 * 92 * @see #connect(int)
95 * @see #connect(int) 93 *
96 * 94 * @throws IOException
97 * @throws IOException 95 */
98 */ 96
99 public synchronized void connect() throws IOException 97 public synchronized void connect() throws IOException {
100 { 98 connect(0);
101 connect(0); 99 }
102 } 100
103 101 /**
104 /** 102 * Establish the connection and block until the first handshake has completed.
105 * Establish the connection and block until the first handshake has completed. 103 * <p>
106 * <p> 104 * Note 1: at least one DSA, RSA or EC hostkey must be set before calling this method.
107 * Note 1: at least one DSA, RSA or EC hostkey must be set before calling this method. 105 * <p>
108 * <p> 106 * Note 2: You must set the callbacks for authentication ({@link #setAuthenticationCallback(ServerAuthenticationCallback)})
109 * Note 2: You must set the callbacks for authentication ({@link #setAuthenticationCallback(ServerAuthenticationCallback)}) 107 * and connection events ({@link #setServerConnectionCallback(ServerConnectionCallback)}).
110 * and connection events ({@link #setServerConnectionCallback(ServerConnectionCallback)}). 108 *
111 * 109 * @see #setPEMHostKey(char[], String)
112 * @see #setPEMHostKey(char[], String) 110 * @see #setPEMHostKey(File, String)
113 * @see #setPEMHostKey(File, String) 111 * @see #setRsaHostKey(RSAPrivateKey)
114 * @see #setRsaHostKey(RSAPrivateKey) 112 * @see #setDsaHostKey(DSAPrivateKey)
115 * @see #setDsaHostKey(DSAPrivateKey) 113 *
116 * 114 * @param timeout_milliseconds Timeout in milliseconds, <code>0</code> means no timeout.
117 * @param timeout_milliseconds Timeout in milliseconds, <code>0</code> means no timeout. 115 * @throws IOException
118 * @throws IOException 116 */
119 */ 117
120 public synchronized void connect(int timeout_milliseconds) throws IOException 118 public synchronized void connect(int timeout_milliseconds) throws IOException {
121 { 119 synchronized (state) {
122 synchronized (state) 120 if (state.cb_conn == null)
123 { 121 throw new IllegalStateException("The callback for connection events has not been set.");
124 if (state.cb_conn == null) 122
125 throw new IllegalStateException("The callback for connection events has not been set."); 123 if (state.cb_auth == null)
126 124 throw new IllegalStateException("The callback for authentication events has not been set.");
127 if (state.cb_auth == null) 125
128 throw new IllegalStateException("The callback for authentication events has not been set."); 126 if (state.tm != null)
129 127 throw new IllegalStateException("The initial handshake has already been started.");
130 if (state.tm != null) 128
131 throw new IllegalStateException("The initial handshake has already been started."); 129 if ((state.next_dsa_key == null) && (state.next_rsa_key == null) && (state.next_ec_key == null))
132 130 throw new IllegalStateException("Neither an RSA nor a DSA nor an EC host key has been specified!");
133 if ((state.next_dsa_key == null) && (state.next_rsa_key == null) && (state.next_ec_key == null)) 131
134 throw new IllegalStateException("Neither an RSA nor a DSA nor an EC host key has been specified!"); 132 state.tm = new ServerTransportManager(state.s);
135 133 }
136 state.tm = new ServerTransportManager(state.s); 134
137 } 135 state.tm.connect(state);
138 136 /* Wait until first KEX has finished */
139 state.tm.connect(state); 137 state.tm.getConnectionInfo(1);
140 138 }
141 /* Wait until first KEX has finished */ 139
142 140 /**
143 state.tm.getConnectionInfo(1); 141 * Retrieve the underlying socket.
144 } 142 *
145 143 * @return the socket that has been passed to the constructor.
146 /** 144 */
147 * Retrieve the underlying socket. 145 public Socket getSocket() {
148 * 146 return state.s;
149 * @return the socket that has been passed to the constructor. 147 }
150 */ 148
151 public Socket getSocket() 149 /**
152 { 150 * Force an asynchronous key re-exchange (the call does not block). The
153 return state.s; 151 * latest values set for MAC, Cipher and DH group exchange parameters will
154 } 152 * be used. If a key exchange is currently in progress, then this method has
155 153 * the only effect that the so far specified parameters will be used for the
156 /** 154 * next (client driven) key exchange. You may call this method only after
157 * Force an asynchronous key re-exchange (the call does not block). The 155 * the initial key exchange has been established.
158 * latest values set for MAC, Cipher and DH group exchange parameters will 156 * <p>
159 * be used. If a key exchange is currently in progress, then this method has 157 * Note: This implementation will never start automatically a key exchange (other than the initial one)
160 * the only effect that the so far specified parameters will be used for the 158 * unless you or the connected SSH-2 client ask for it.
161 * next (client driven) key exchange. You may call this method only after 159 *
162 * the initial key exchange has been established. 160 * @throws IOException
163 * <p> 161 * In case of any failure behind the scenes.
164 * Note: This implementation will never start automatically a key exchange (other than the initial one) 162 */
165 * unless you or the connected SSH-2 client ask for it. 163
166 * 164 public synchronized void forceKeyExchange() throws IOException {
167 * @throws IOException 165 synchronized (state) {
168 * In case of any failure behind the scenes. 166 if (state.tm == null)
169 */ 167 throw new IllegalStateException(
170 public synchronized void forceKeyExchange() throws IOException 168 "Cannot force another key exchange, you need to start the key exchange first.");
171 { 169
172 synchronized (state) 170 state.tm.forceKeyExchange(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
173 { 171 }
174 if (state.tm == null) 172 }
175 throw new IllegalStateException( 173
176 "Cannot force another key exchange, you need to start the key exchange first."); 174 /**
177 175 * Returns a {@link ConnectionInfo} object containing the details of
178 state.tm.forceKeyExchange(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key, state.next_ec_key); 176 * the connection. May be called as soon as the first key exchange has been
179 } 177 * started. The method blocks in case the first key exchange has not been completed.
180 } 178 * <p>
181 179 * Note: upon return of this method, authentication may still be pending.
182 /** 180 *
183 * Returns a {@link ConnectionInfo} object containing the details of 181 * @return A {@link ConnectionInfo} object.
184 * the connection. May be called as soon as the first key exchange has been 182 * @throws IOException
185 * started. The method blocks in case the first key exchange has not been completed. 183 * In case of any failure behind the scenes; e.g., first key exchange was aborted.
186 * <p> 184 */
187 * Note: upon return of this method, authentication may still be pending. 185
188 * 186 public synchronized ConnectionInfo getConnectionInfo() throws IOException {
189 * @return A {@link ConnectionInfo} object. 187 synchronized (state) {
190 * @throws IOException 188 if (state.tm == null)
191 * In case of any failure behind the scenes; e.g., first key exchange was aborted. 189 throw new IllegalStateException(
192 */ 190 "Cannot get details of connection, you need to start the key exchange first.");
193 public synchronized ConnectionInfo getConnectionInfo() throws IOException 191 }
194 { 192
195 synchronized (state) 193 return state.tm.getConnectionInfo(1);
196 { 194 }
197 if (state.tm == null) 195
198 throw new IllegalStateException( 196 /**
199 "Cannot get details of connection, you need to start the key exchange first."); 197 * Change the current DSA hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with
200 } 198 * the client.
201 199 * <p>
202 return state.tm.getConnectionInfo(1); 200 * Note: You can change an existing DSA hostkey after the initial kex exchange (the new value will
203 } 201 * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the
204 202 * current DSA key, otherwise the next key exchange may fail in case the client supports only DSA hostkeys.
205 /** 203 *
206 * Change the current DSA hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with 204 * @param dsa_hostkey
207 * the client. 205 */
208 * <p> 206
209 * Note: You can change an existing DSA hostkey after the initial kex exchange (the new value will 207 public synchronized void setDsaHostKey(KeyPair dsa_hostkey) {
210 * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the 208 synchronized (state) {
211 * current DSA key, otherwise the next key exchange may fail in case the client supports only DSA hostkeys. 209 if ((dsa_hostkey == null) && (state.next_dsa_key != null) && (state.tm != null))
212 * 210 throw new IllegalStateException("Cannot remove DSA hostkey after first key exchange.");
213 * @param dsa_hostkey 211
214 */ 212 state.next_dsa_key = dsa_hostkey;
215 public synchronized void setDsaHostKey(KeyPair dsa_hostkey) 213 fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
216 { 214 }
217 synchronized (state) 215 }
218 { 216
219 if ((dsa_hostkey == null) && (state.next_dsa_key != null) && (state.tm != null)) 217 /**
220 throw new IllegalStateException("Cannot remove DSA hostkey after first key exchange."); 218 * Change the current RSA hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with
221 219 * the client.
222 state.next_dsa_key = dsa_hostkey; 220 * <p>
223 fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key); 221 * Note: You can change an existing RSA hostkey after the initial kex exchange (the new value will
224 } 222 * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the
225 } 223 * current RSA key, otherwise the next key exchange may fail in case the client supports only RSA hostkeys.
226 224 *
227 /** 225 * @param rsa_hostkey
228 * Change the current RSA hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with 226 */
229 * the client. 227
230 * <p> 228 public synchronized void setRsaHostKey(KeyPair rsa_hostkey) {
231 * Note: You can change an existing RSA hostkey after the initial kex exchange (the new value will 229 synchronized (state) {
232 * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the 230 if ((rsa_hostkey == null) && (state.next_rsa_key != null) && (state.tm != null))
233 * current RSA key, otherwise the next key exchange may fail in case the client supports only RSA hostkeys. 231 throw new IllegalStateException("Cannot remove RSA hostkey after first key exchange.");
234 * 232
235 * @param rsa_hostkey 233 state.next_rsa_key = rsa_hostkey;
236 */ 234 fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
237 public synchronized void setRsaHostKey(KeyPair rsa_hostkey) 235 }
238 { 236 }
239 synchronized (state) 237
240 { 238 /**
241 if ((rsa_hostkey == null) && (state.next_rsa_key != null) && (state.tm != null)) 239 * Change the current EC hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with
242 throw new IllegalStateException("Cannot remove RSA hostkey after first key exchange."); 240 * the client.
243 241 * <p>
244 state.next_rsa_key = rsa_hostkey; 242 * Note: You can change an existing EC hostkey after the initial kex exchange (the new value will
245 fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key); 243 * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the
246 } 244 * current EC key, otherwise the next key exchange may fail in case the client supports only EC hostkeys.
247 } 245 *
248 246 * @param rsa_hostkey
249 /** 247 */
250 * Change the current EC hostkey. Either a DSA or RSA or EC private key must be set for a successful handshake with 248
251 * the client. 249 public synchronized void setEcHostKey(KeyPair ec_hostkey) {
252 * <p> 250 synchronized (state) {
253 * Note: You can change an existing EC hostkey after the initial kex exchange (the new value will 251 if ((ec_hostkey == null) && (state.next_ec_key != null) && (state.tm != null))
254 * be used during the next server initiated key exchange), but you cannot remove (i.e., set to <code>null</code>) the 252 throw new IllegalStateException("Cannot remove EC hostkey after first key exchange.");
255 * current EC key, otherwise the next key exchange may fail in case the client supports only EC hostkeys. 253
256 * 254 state.next_ec_key = ec_hostkey;
257 * @param rsa_hostkey 255 fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key);
258 */ 256 }
259 public synchronized void setEcHostKey(KeyPair ec_hostkey) 257 }
260 { 258
261 synchronized (state) 259 /**
262 { 260 * Utility method that loads a PEM based hostkey (either RSA or DSA based) and
263 if ((ec_hostkey == null) && (state.next_ec_key != null) && (state.tm != null)) 261 * calls either <code>setRsaHostKey()</code> or <code>setDsaHostKey()</code>.
264 throw new IllegalStateException("Cannot remove EC hostkey after first key exchange."); 262 *
265 263 * @param pemdata The PEM data
266 state.next_ec_key = ec_hostkey; 264 * @param password Password, may be null in case the PEM data is not password protected
267 fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key, state.next_ec_key); 265 * @throws IOException In case of any error.
268 } 266 */
269 } 267 public void setPEMHostKey(char[] pemdata, String password) throws IOException {
270 268 KeyPair pair = PEMDecoder.decode(pemdata, password);
271 /**
272 * Utility method that loads a PEM based hostkey (either RSA or DSA based) and
273 * calls either <code>setRsaHostKey()</code> or <code>setDsaHostKey()</code>.
274 *
275 * @param pemdata The PEM data
276 * @param password Password, may be null in case the PEM data is not password protected
277 * @throws IOException In case of any error.
278 */
279 public void setPEMHostKey(char[] pemdata, String password) throws IOException
280 {
281 KeyPair pair = PEMDecoder.decode(pemdata, password);
282 PrivateKey key = pair.getPrivate(); 269 PrivateKey key = pair.getPrivate();
283 270
284 if (key instanceof DSAPrivateKey) setDsaHostKey(pair); 271 if (key instanceof DSAPrivateKey) setDsaHostKey(pair);
285 272
286 if (key instanceof RSAPrivateKey) setRsaHostKey(pair); 273 if (key instanceof RSAPrivateKey) setRsaHostKey(pair);
287 274
288 if (key instanceof ECPrivateKey) setEcHostKey(pair); 275 if (key instanceof ECPrivateKey) setEcHostKey(pair);
289 } 276 }
290 277
291 /** 278 /**
292 * Utility method that loads a hostkey from a PEM file (either RSA or DSA based) and 279 * Utility method that loads a hostkey from a PEM file (either RSA or DSA based) and
293 * calls either <code>setRsaHostKey()</code> or <code>setDsaHostKey()</code>. 280 * calls either <code>setRsaHostKey()</code> or <code>setDsaHostKey()</code>.
294 * 281 *
295 * @param pemFile The PEM file 282 * @param pemFile The PEM file
296 * @param password Password, may be null in case the PEM file is not password protected 283 * @param password Password, may be null in case the PEM file is not password protected
297 * @throws IOException 284 * @throws IOException
298 */ 285 */
299 public void setPEMHostKey(File pemFile, String password) throws IOException 286 public void setPEMHostKey(File pemFile, String password) throws IOException {
300 { 287 if (pemFile == null)
301 if (pemFile == null) 288 throw new IllegalArgumentException("pemfile argument is null");
302 throw new IllegalArgumentException("pemfile argument is null"); 289
303 290 char[] buff = new char[256];
304 char[] buff = new char[256]; 291 CharArrayWriter cw = new CharArrayWriter();
305 292 FileReader fr = new FileReader(pemFile);
306 CharArrayWriter cw = new CharArrayWriter(); 293
307 294 while (true) {
308 FileReader fr = new FileReader(pemFile); 295 int len = fr.read(buff);
309 296
310 while (true) 297 if (len < 0)
311 { 298 break;
312 int len = fr.read(buff); 299
313 if (len < 0) 300 cw.write(buff, 0, len);
314 break; 301 }
315 cw.write(buff, 0, len); 302
316 } 303 fr.close();
317 304 setPEMHostKey(cw.toCharArray(), password);
318 fr.close(); 305 }
319 306
320 setPEMHostKey(cw.toCharArray(), password); 307 private void fixCryptoWishList(CryptoWishList next_cryptoWishList, KeyPair next_dsa_key, KeyPair next_rsa_key, KeyPair next_ec_key) {
321 }
322
323 private void fixCryptoWishList(CryptoWishList next_cryptoWishList, KeyPair next_dsa_key, KeyPair next_rsa_key, KeyPair next_ec_key)
324 {
325 List<String> algos = new ArrayList<String>(); 308 List<String> algos = new ArrayList<String>();
326 if (next_ec_key != null) algos.add("ecdsa-sha2-nistp521"); 309
327 if (next_ec_key != null) algos.add("ecdsa-sha2-nistp384"); 310 if (next_ec_key != null) algos.add("ecdsa-sha2-nistp521");
328 if (next_ec_key != null) algos.add("ecdsa-sha2-nistp256"); 311
329 if (next_dsa_key != null) algos.add("ssh-dss"); 312 if (next_ec_key != null) algos.add("ecdsa-sha2-nistp384");
330 if (next_rsa_key != null) algos.add("ssh-rsa"); 313
331 next_cryptoWishList.serverHostKeyAlgorithms = new String[algos.size()]; 314 if (next_ec_key != null) algos.add("ecdsa-sha2-nistp256");
315
316 if (next_dsa_key != null) algos.add("ssh-dss");
317
318 if (next_rsa_key != null) algos.add("ssh-rsa");
319
320 next_cryptoWishList.serverHostKeyAlgorithms = new String[algos.size()];
332 algos.toArray(next_cryptoWishList.serverHostKeyAlgorithms); 321 algos.toArray(next_cryptoWishList.serverHostKeyAlgorithms);
333 } 322 }
334 323
335 /** 324 /**
336 * Callback interface with methods that will be called upon events 325 * Callback interface with methods that will be called upon events
337 * generated by the client (e.g., client opens a new Session which results in a <code>ServerSession</code>). 326 * generated by the client (e.g., client opens a new Session which results in a <code>ServerSession</code>).
338 * <p> 327 * <p>
339 * Note: This must be set before the first handshake. 328 * Note: This must be set before the first handshake.
340 * 329 *
341 * @param cb The callback implementation 330 * @param cb The callback implementation
342 */ 331 */
343 public synchronized void setServerConnectionCallback(ServerConnectionCallback cb) 332
344 { 333 public synchronized void setServerConnectionCallback(ServerConnectionCallback cb) {
345 synchronized (state) 334 synchronized (state) {
346 { 335 state.cb_conn = cb;
347 state.cb_conn = cb; 336 }
348 } 337 }
349 } 338
350 339 /**
351 /** 340 * Callback interface with methods that will be called upon authentication events.
352 * Callback interface with methods that will be called upon authentication events. 341 * <p>
353 * <p> 342 * Note: This must be set before the first handshake.
354 * Note: This must be set before the first handshake. 343 *
355 * 344 * @param cb The callback implementation
356 * @param cb The callback implementation 345 */
357 */ 346
358 public synchronized void setAuthenticationCallback(ServerAuthenticationCallback cb) 347 public synchronized void setAuthenticationCallback(ServerAuthenticationCallback cb) {
359 { 348 synchronized (state) {
360 synchronized (state) 349 state.cb_auth = cb;
361 { 350 }
362 state.cb_auth = cb; 351 }
363 } 352
364 } 353 /**
365 354 * Close the connection to the SSH-2 server. All assigned sessions will be
366 /** 355 * closed, too. Can be called at any time. Don't forget to call this once
367 * Close the connection to the SSH-2 server. All assigned sessions will be 356 * you don't need a connection anymore - otherwise the receiver thread may
368 * closed, too. Can be called at any time. Don't forget to call this once 357 * run forever.
369 * you don't need a connection anymore - otherwise the receiver thread may 358 */
370 * run forever. 359 public void close() {
371 */ 360 synchronized (state) {
372 public void close() 361 if (state.cm != null)
373 { 362 state.cm.closeAllChannels();
374 synchronized (state) 363
375 { 364 if (state.tm != null) {
376 if (state.cm != null) 365 state.tm.close();
377 state.cm.closeAllChannels(); 366 }
378 367 }
379 if (state.tm != null) 368 }
380 { 369
381 state.tm.close(); 370 public void close(IOException t) {
382 } 371 synchronized (state) {
383 } 372 if (state.cm != null)
384 } 373 state.cm.closeAllChannels();
385 374
386 public void close(IOException t) 375 if (state.tm != null) {
387 { 376 state.tm.close(t);
388 synchronized (state) 377 }
389 { 378 }
390 if (state.cm != null) 379 }
391 state.cm.closeAllChannels();
392
393 if (state.tm != null)
394 {
395 state.tm.close(t);
396 }
397 }
398 }
399 } 380 }