From 50403483da9f2f1098d359765e3d4d138154d301 Mon Sep 17 00:00:00 2001 From: Jeroen van Erp Date: Fri, 18 Mar 2016 13:24:33 +0100 Subject: [PATCH] Implemented switch for waiting on server ident before sending client ident. (Fixes #118) --- src/main/java/net/schmizz/sshj/Config.java | 20 ++++++++++ .../java/net/schmizz/sshj/ConfigImpl.java | 14 ++++++- .../schmizz/sshj/transport/TransportImpl.java | 38 +++++++++++++------ 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/schmizz/sshj/Config.java b/src/main/java/net/schmizz/sshj/Config.java index 7e77ec0f3..a0b156e7f 100644 --- a/src/main/java/net/schmizz/sshj/Config.java +++ b/src/main/java/net/schmizz/sshj/Config.java @@ -155,4 +155,24 @@ public interface Config { * @param keepAliveProvider keep-alive provider */ void setKeepAliveProvider(KeepAliveProvider keepAliveProvider); + + /** + * Gets whether the client should first wait for a received server ident, before sending the client ident. + *

+ * NB: This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident. + * + * The default value is set to false. + * + * @return Whether to first wait for the server ident. + */ + boolean isWaitForServerIdentBeforeSendingClientIdent(); + + /** + * Sets whether the SSH client should wait for a received server ident, before sending the client ident. + *

+ * NB: This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident. + + * @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident. + */ + void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent); } diff --git a/src/main/java/net/schmizz/sshj/ConfigImpl.java b/src/main/java/net/schmizz/sshj/ConfigImpl.java index d15384ece..9fb253f7f 100644 --- a/src/main/java/net/schmizz/sshj/ConfigImpl.java +++ b/src/main/java/net/schmizz/sshj/ConfigImpl.java @@ -44,6 +44,8 @@ public class ConfigImpl private List> signatureFactories; private List> fileKeyProviderFactories; + private boolean waitForServerIdentBeforeSendingClientIdent = false; + @Override public List> getCipherFactories() { return cipherFactories; @@ -157,4 +159,14 @@ public KeepAliveProvider getKeepAliveProvider() { public void setKeepAliveProvider(KeepAliveProvider keepAliveProvider) { this.keepAliveProvider = keepAliveProvider; } -} \ No newline at end of file + + @Override + public boolean isWaitForServerIdentBeforeSendingClientIdent() { + return waitForServerIdentBeforeSendingClientIdent; + } + + @Override + public void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent) { + this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent; + } +} diff --git a/src/main/java/net/schmizz/sshj/transport/TransportImpl.java b/src/main/java/net/schmizz/sshj/transport/TransportImpl.java index 80b2b01fb..c2e790cec 100644 --- a/src/main/java/net/schmizz/sshj/transport/TransportImpl.java +++ b/src/main/java/net/schmizz/sshj/transport/TransportImpl.java @@ -152,19 +152,15 @@ public void init(String remoteHost, int remotePort, InputStream in, OutputStream try { - log.info("Client identity string: {}", clientID); - connInfo.out.write((clientID + "\r\n").getBytes(IOUtils.UTF8)); - connInfo.out.flush(); - - // Read server's ID - final Buffer.PlainBuffer buf = new Buffer.PlainBuffer(); - while ((serverID = readIdentification(buf)).isEmpty()) { - int b = connInfo.in.read(); - if (b == -1) - throw new TransportException("Server closed connection during identification exchange"); - buf.putByte((byte) b); + if (config.isWaitForServerIdentBeforeSendingClientIdent()) { + receiveServerIdent(); + sendClientIdent(); + } else { + sendClientIdent(); + receiveServerIdent(); } + log.info("Server identity string: {}", serverID); } catch (IOException e) { @@ -174,6 +170,26 @@ public void init(String remoteHost, int remotePort, InputStream in, OutputStream reader.start(); } + private void receiveServerIdent() throws IOException { + final Buffer.PlainBuffer buf = new Buffer.PlainBuffer(); + while ((serverID = readIdentification(buf)).isEmpty()) { + int b = connInfo.in.read(); + if (b == -1) + throw new TransportException("Server closed connection during identification exchange"); + buf.putByte((byte) b); + } + } + + /** + * Receive the server identification string. + * @throws IOException If there was an error writing to the outputstream. + */ + private void sendClientIdent() throws IOException { + log.info("Client identity string: {}", clientID); + connInfo.out.write((clientID + "\r\n").getBytes(IOUtils.UTF8)); + connInfo.out.flush(); + } + /** * Reads the identification string from the SSH server. This is the very first string that is sent upon connection * by the server. It takes the form of, e.g. "SSH-2.0-OpenSSH_ver".