diff --git a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs index c804c291f..68c3c334c 100644 --- a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs @@ -19,8 +19,22 @@ internal interface IProtocolVersionExchange /// SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout); + + /// + /// Performs the SSH protocol version exchange. + /// + /// The identification string of the SSH client. + /// A connected to the server. + /// The maximum time to wait for the server to respond. + /// Allow server to identify itself first. + /// + /// The SSH identification of the server. + /// + SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification); + #if FEATURE_TAP System.Threading.Tasks.Task StartAsync(string clientVersion, Socket socket, System.Threading.CancellationToken cancellationToken); #endif + } } diff --git a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs index 4e6957c10..5f41f50c2 100644 --- a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs @@ -41,9 +41,27 @@ internal class ProtocolVersionExchange : IProtocolVersionExchange /// public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout) { - // Immediately send the identification string since the spec states both sides MUST send an identification string - // when the connection has been established - SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A")); + return Start(clientVersion, socket, timeout, false); + } + + /// + /// Performs the SSH protocol version exchange. + /// + /// The identification string of the SSH client. + /// A connected to the server. + /// The maximum time to wait for the server to respond. + /// Allow server to identify itself first. + /// + /// The SSH identification of the server. + /// + public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification = false) + { + if (!lazyIdentification) + { + // Immediately send the identification string since the spec states both sides MUST send an identification string + // when the connection has been established + SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A")); + } var bytesReceived = new List(); @@ -75,6 +93,12 @@ public SshIdentification Start(string clientVersion, Socket socket, TimeSpan tim var identificationMatch = ServerVersionRe.Match(line); if (identificationMatch.Success) { + if (lazyIdentification) + { + // Send identification only after server identification has been validated. + SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A")); + } + return new SshIdentification(GetGroupValue(identificationMatch, "protoversion"), GetGroupValue(identificationMatch, "softwareversion"), GetGroupValue(identificationMatch, "comments")); diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index af8e1ca5c..ebb0dc3ac 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -183,6 +183,14 @@ public class ConnectionInfo : IConnectionInfoInternal /// public int MaxSessions { get; set; } + /// + /// Gets or sets if the client should identify itself later. + /// + /// + /// false, the default for strict RFC4253 compliance where both sides identify at the same time. true if the client should wait for the server identification. + /// + public bool LazyIdentification { get; set; } + /// /// Occurs when authentication banner is sent by the server. /// @@ -432,6 +440,8 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy ProxyPassword = proxyPassword; AuthenticationMethods = authenticationMethods; + + LazyIdentification = false; } /// diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 0846cb2cd..5966eeec6 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -591,7 +591,7 @@ public void Connect() .Connect(ConnectionInfo); var serverIdentification = _serviceFactory.CreateProtocolVersionExchange() - .Start(ClientVersion, _socket, ConnectionInfo.Timeout); + .Start(ClientVersion, _socket, ConnectionInfo.Timeout, ConnectionInfo.LazyIdentification); // Set connection versions ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();