use of com.trilead.ssh2.util.TimeoutService.TimeoutToken in project intellij-community by JetBrains.
the class Connection method connect.
/**
* Connect to the SSH-2 server and, as soon as the server has presented its
* host key, use the
* {@link ServerHostKeyVerifier#verifyServerHostKey(String, int, String,
* byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method of the
* <code>verifier</code> to ask for permission to proceed. If
* <code>verifier</code> is <code>null</code>, then any host key will
* be accepted - this is NOT recommended, since it makes man-in-the-middle
* attackes VERY easy (somebody could put a proxy SSH server between you and
* the real server).
* <p>
* Note: The verifier will be called before doing any crypto calculations
* (i.e., diffie-hellman). Therefore, if you don't like the presented host
* key then no CPU cycles are wasted (and the evil server has less
* information about us).
* <p>
* However, it is still possible that the server presented a fake host key:
* the server cheated (typically a sign for a man-in-the-middle attack) and
* is not able to generate a signature that matches its host key. Don't
* worry, the library will detect such a scenario later when checking the
* signature (the signature cannot be checked before having completed the
* diffie-hellman exchange).
* <p>
* Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String, int,
* String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method will
* *NOT* be called from the current thread, the call is being made from a
* background thread (there is a background dispatcher thread for every
* established connection).
* <p>
* Note 3: This method will block as long as the key exchange of the
* underlying connection has not been completed (and you have not specified
* any timeouts).
* <p>
* Note 4: If you want to re-use a connection object that was successfully
* connected, then you must call the {@link #close()} method before invoking
* <code>connect()</code> again.
*
* @param verifier
* An object that implements the {@link ServerHostKeyVerifier}
* interface. Pass <code>null</code> to accept any server host
* key - NOT recommended.
*
* @param connectTimeout
* Connect the underlying TCP socket to the server with the given
* timeout value (non-negative, in milliseconds). Zero means no
* timeout. If a proxy is being used (see
* {@link #setProxyData(ProxyData)}), then this timeout is used
* for the connection establishment to the proxy.
*
* @param kexTimeout
* Timeout for complete connection establishment (non-negative,
* in milliseconds). Zero means no timeout. The timeout counts
* from the moment you invoke the connect() method and is
* cancelled as soon as the first key-exchange round has
* finished. It is possible that the timeout event will be fired
* during the invocation of the <code>verifier</code> callback,
* but it will only have an effect after the
* <code>verifier</code> returns.
*
* @return A {@link ConnectionInfo} object containing the details of the
* established connection.
*
* @throws IOException
* If any problem occurs, e.g., the server's host key is not
* accepted by the <code>verifier</code> or there is problem
* during the initial crypto setup (e.g., the signature sent by
* the server is wrong).
* <p>
* In case of a timeout (either connectTimeout or kexTimeout) a
* SocketTimeoutException is thrown.
* <p>
* An exception may also be thrown if the connection was already
* successfully connected (no matter if the connection broke in
* the mean time) and you invoke <code>connect()</code> again
* without having called {@link #close()} first.
* <p>
* If a HTTP proxy is being used and the proxy refuses the
* connection, then a {@link HTTPProxyException} may be thrown,
* which contains the details returned by the proxy. If the
* proxy is buggy and does not return a proper HTTP response,
* then a normal IOException is thrown instead.
*/
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout) throws IOException {
final class TimeoutState {
boolean isCancelled = false;
boolean timeoutSocketClosed = false;
}
if (tm != null)
throw new IOException("Connection to " + hostname + " is already in connected state!");
if (connectTimeout < 0)
throw new IllegalArgumentException("connectTimeout must be non-negative!");
if (kexTimeout < 0)
throw new IllegalArgumentException("kexTimeout must be non-negative!");
final TimeoutState state = new TimeoutState();
tm = new TransportManager(hostname, port);
tm.setConnectionMonitors(connectionMonitors);
synchronized (tm) {
/* We could actually synchronize on anything. */
}
try {
TimeoutToken token = null;
if (kexTimeout > 0) {
final Runnable timeoutHandler = new Runnable() {
public void run() {
synchronized (state) {
if (state.isCancelled)
return;
state.timeoutSocketClosed = true;
tm.close(new SocketTimeoutException("The connect timeout expired"), false);
}
}
};
long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
}
try {
tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
} catch (SocketTimeoutException se) {
throw (SocketTimeoutException) new SocketTimeoutException("The connect() operation on the socket timed out.").initCause(se);
}
tm.setTcpNoDelay(tcpNoDelay);
/* Wait until first KEX has finished */
ConnectionInfo ci = tm.getConnectionInfo(1);
if (token != null) {
TimeoutService.cancelTimeoutHandler(token);
synchronized (state) {
if (state.timeoutSocketClosed)
throw new IOException("This exception will be replaced by the one below =)");
/*
* Just in case the "cancelTimeoutHandler" invocation came
* just a little bit too late but the handler did not enter
* the semaphore yet - we can still stop it.
*/
state.isCancelled = true;
}
}
return ci;
} catch (SocketTimeoutException ste) {
throw ste;
} catch (IOException e1) {
/* This will also invoke any registered connection monitors */
close(new Throwable("There was a problem during connect."), false);
synchronized (state) {
/*
* Show a clean exception, not something like "the socket is
* closed!?!"
*/
if (state.timeoutSocketClosed)
throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
}
/* Do not wrap a HTTPProxyException */
if (e1 instanceof HTTPProxyException)
throw e1;
throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port).initCause(e1);
}
}
Aggregations