From 4906b64e7a5f80ee76e6cc303dbe9ab12c555679 Mon Sep 17 00:00:00 2001 From: drieseng Date: Mon, 1 Jun 2020 09:52:49 +0200 Subject: [PATCH] Immediately sent identification string when connection is established. Fixes #689. --- src/Renci.SshNet.Tests/Classes/SessionTest.cs | 49 +++++++++++++++++++ src/Renci.SshNet/Session.cs | 40 ++++++++------- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest.cs b/src/Renci.SshNet.Tests/Classes/SessionTest.cs index 29e88746..de523769 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest.cs @@ -3,9 +3,11 @@ using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; using Renci.SshNet.Tests.Common; using Renci.SshNet.Tests.Properties; @@ -98,6 +100,53 @@ namespace Renci.SshNet.Tests.Classes } } + [TestMethod] + public void ConnectShouldImmediatelySendIdentificationStringWhenConnectionHasBeenEstablised() + { + var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); + var connectionInfo = CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5)); + + using (var serverStub = new AsyncSocketListener(serverEndPoint)) + { + serverStub.Connected += socket => + { + var identificationBytes = new byte[2048]; + var bytesReceived = socket.Receive(identificationBytes); + + if (bytesReceived > 0) + { + var identificationSttring = Encoding.ASCII.GetString(identificationBytes, 0, bytesReceived); + Console.WriteLine("STRING=" + identificationSttring); + Console.WriteLine("DONE"); + + socket.Send(Encoding.ASCII.GetBytes("\r\n")); + socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n")); + socket.Send(Encoding.ASCII.GetBytes("SSH-666-SshStub\r\n")); + } + + socket.Shutdown(SocketShutdown.Send); + }; + serverStub.Start(); + + using (var session = new Session(connectionInfo, _serviceFactoryMock.Object)) + { + try + { + session.Connect(); + Assert.Fail(); + } + catch (SshConnectionException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Server version '666' is not supported.", ex.Message); + + Assert.AreEqual("SSH-666-SshStub", connectionInfo.ServerVersion); + } + } + } + } + + [TestMethod] public void ConnectShouldSupportProtocolIdentificationStringThatDoesNotEndWithCrlf() { diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 89779685..12c4f5b2 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -577,14 +577,14 @@ namespace Renci.SshNet lock (this) { - // If connected don't connect again + // If connected don't connect again if (IsConnected) return; - // reset connection specific information + // Reset connection specific information Reset(); - // Build list of available messages while connecting + // Build list of available messages while connecting _sshMessageFactory = new SshMessageFactory(); switch (ConnectionInfo.ProxyType) @@ -606,10 +606,14 @@ namespace Renci.SshNet break; } + // 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(string.Format(CultureInfo.InvariantCulture, "{0}\x0D\x0A", ClientVersion))); + Match versionMatch; - // Get server version from the server, - // ignore text lines which are sent before if any + // Get server version from the server, + // ignore text lines which are sent before if any while (true) { var serverVersion = SocketReadLine(_socket, ConnectionInfo.Timeout); @@ -623,11 +627,11 @@ namespace Renci.SshNet } } - // Set connection versions + // Set connection versions ConnectionInfo.ServerVersion = ServerVersion; ConnectionInfo.ClientVersion = ClientVersion; - // Get server SSH version + // Get server SSH version var version = versionMatch.Result("${protoversion}"); var softwareName = versionMatch.Result("${softwareversion}"); @@ -639,9 +643,7 @@ namespace Renci.SshNet throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", version), DisconnectReason.ProtocolVersionNotSupported); } - SocketAbstraction.Send(_socket, Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\x0D\x0A", ClientVersion))); - - // Register Transport response messages + // Register Transport response messages RegisterMessage("SSH_MSG_DISCONNECT"); RegisterMessage("SSH_MSG_IGNORE"); RegisterMessage("SSH_MSG_UNIMPLEMENTED"); @@ -650,29 +652,29 @@ namespace Renci.SshNet RegisterMessage("SSH_MSG_KEXINIT"); RegisterMessage("SSH_MSG_NEWKEYS"); - // Some server implementations might sent this message first, prior establishing encryption algorithm + // Some server implementations might sent this message first, prior to establishing encryption algorithm RegisterMessage("SSH_MSG_USERAUTH_BANNER"); - // mark the message listener threads as started + // Mark the message listener threads as started _messageListenerCompleted.Reset(); - // Start incoming request listener + // Start incoming request listener ThreadAbstraction.ExecuteThread(() => MessageListener()); - // Wait for key exchange to be completed + // Wait for key exchange to be completed WaitOnHandle(_keyExchangeCompletedWaitHandle); - // If sessionId is not set then its not connected + // If sessionId is not set then its not connected if (SessionId == null) { Disconnect(); return; } - // Request user authorization service + // Request user authorization service SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication)); - // Wait for service to be accepted + // Wait for service to be accepted WaitOnHandle(_serviceAccepted); if (string.IsNullOrEmpty(ConnectionInfo.Username)) @@ -687,7 +689,7 @@ namespace Renci.SshNet ConnectionInfo.Authenticate(this, _serviceFactory); _isAuthenticated = true; - // Register Connection messages + // Register Connection messages RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); RegisterMessage("SSH_MSG_REQUEST_FAILURE"); RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); @@ -2003,6 +2005,8 @@ namespace Renci.SshNet break; } + Console.WriteLine("RECEIVED MESSAGe " + message.GetType()); + // process message message.Process(this); }