using System; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; using Renci.SshNet.Tests.Properties; namespace Renci.SshNet.Tests.Classes { /// /// Provides functionality to connect and interact with SSH server. /// [TestClass] public partial class SessionTest : TestBase { private Mock _serviceFactoryMock; protected override void OnInit() { base.OnInit(); _serviceFactoryMock = new Mock(MockBehavior.Strict); } [TestMethod] public void ConstructorShouldThrowArgumentNullExceptionWhenConnectionInfoIsNull() { ConnectionInfo connectionInfo = null; var serviceFactory = new Mock(MockBehavior.Strict).Object; try { new Session(connectionInfo, serviceFactory); Assert.Fail(); } catch (ArgumentNullException ex) { Assert.IsNull(ex.InnerException); Assert.AreEqual("connectionInfo", ex.ParamName); } } [TestMethod] public void ConstructorShouldThrowArgumentNullExceptionWhenServiceFactoryIsNull() { var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); var connectionInfo = CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5)); IServiceFactory serviceFactory = null; try { new Session(connectionInfo, serviceFactory); Assert.Fail(); } catch (ArgumentNullException ex) { Assert.IsNull(ex.InnerException); Assert.AreEqual("serviceFactory", ex.ParamName); } } [TestMethod] public void ConnectShouldSkipLinesBeforeProtocolIdentificationString() { var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); var connectionInfo = CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5)); using (var serverStub = new AsyncSocketListener(serverEndPoint)) { serverStub.Connected += socket => { 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() { var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); var connectionInfo = CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5)); using (var serverStub = new AsyncSocketListener(serverEndPoint)) { serverStub.Connected += socket => { socket.Send(Encoding.ASCII.GetBytes("\r\n")); socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n")); socket.Send(Encoding.ASCII.GetBytes("SSH-666-SshStub")); 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 ConnectShouldThrowSshOperationExceptionWhenServerDoesNotRespondWithinConnectionTimeout() { var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); var timeout = TimeSpan.FromMilliseconds(500); Socket clientSocket = null; using (var serverStub = new AsyncSocketListener(serverEndPoint)) { serverStub.Connected += socket => { socket.Send(Encoding.ASCII.GetBytes("\r\n")); socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n")); clientSocket = socket; }; serverStub.Start(); using (var session = new Session(CreateConnectionInfo(serverEndPoint, TimeSpan.FromMilliseconds(500)), _serviceFactoryMock.Object)) { try { session.Connect(); Assert.Fail(); } catch (SshOperationTimeoutException ex) { Assert.IsNull(ex.InnerException); Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, "Socket read operation has timed out after {0:F0} milliseconds.", timeout.TotalMilliseconds), ex.Message); Assert.IsNotNull(clientSocket); Assert.IsTrue(clientSocket.Connected); // shut down socket clientSocket.Shutdown(SocketShutdown.Send); } } } } [TestMethod] public void ConnectShouldSshConnectionExceptionWhenServerResponseDoesNotContainProtocolIdentificationString() { var serverEndPoint = new IPEndPoint(IPAddress.Loopback, 8122); // response ends with CRLF using (var serverStub = new AsyncSocketListener(serverEndPoint)) { serverStub.Connected += socket => { socket.Send(Encoding.ASCII.GetBytes("\r\n")); socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n")); socket.Shutdown(SocketShutdown.Send); }; serverStub.Start(); using (var session = new Session(CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5)), _serviceFactoryMock.Object)) { try { session.Connect(); Assert.Fail(); } catch (SshConnectionException ex) { Assert.IsNull(ex.InnerException); Assert.AreEqual("Server response does not contain SSH protocol identification.", ex.Message); } } } // response does not end with CRLF using (var serverStub = new AsyncSocketListener(serverEndPoint)) { serverStub.Connected += socket => { socket.Send(Encoding.ASCII.GetBytes("\r\n")); socket.Send(Encoding.ASCII.GetBytes("WELCOME banner")); socket.Shutdown(SocketShutdown.Send); }; serverStub.Start(); using (var session = new Session(CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5)), _serviceFactoryMock.Object)) { try { session.Connect(); Assert.Fail(); } catch (SshConnectionException ex) { Assert.IsNull(ex.InnerException); Assert.AreEqual("Server response does not contain SSH protocol identification.", ex.Message); } } } // last line is empty using (var serverStub = new AsyncSocketListener(serverEndPoint)) { serverStub.Connected += socket => { socket.Send(Encoding.ASCII.GetBytes("\r\n")); socket.Send(Encoding.ASCII.GetBytes("WELCOME banner\r\n")); socket.Send(Encoding.ASCII.GetBytes("\r\n")); socket.Shutdown(SocketShutdown.Send); }; serverStub.Start(); using (var session = new Session(CreateConnectionInfo(serverEndPoint, TimeSpan.FromSeconds(5)), _serviceFactoryMock.Object)) { try { session.Connect(); Assert.Fail(); } catch (SshConnectionException ex) { Assert.IsNull(ex.InnerException); Assert.AreEqual("Server response does not contain SSH protocol identification.", ex.Message); } } } } [TestMethod] public void Connect_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() { var connectionInfo = new ConnectionInfo("invalid.", 40, "user", new KeyboardInteractiveAuthenticationMethod("user")); var session = new Session(connectionInfo, _serviceFactoryMock.Object); try { session.Connect(); Assert.Fail(); } catch (SocketException ex) { Assert.AreEqual(ex.ErrorCode, (int)SocketError.HostNotFound); } } [TestMethod] public void Connect_ProxyHostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() { var connectionInfo = new ConnectionInfo("localhost", 40, "user", ProxyTypes.Http, "invalid.", 80, "proxyUser", "proxyPwd", new KeyboardInteractiveAuthenticationMethod("user")); var session = new Session(connectionInfo, _serviceFactoryMock.Object); try { session.Connect(); Assert.Fail(); } catch (SocketException ex) { Assert.AreEqual(ex.ErrorCode, (int)SocketError.HostNotFound); } } [TestMethod] public void DisconnectShouldNotThrowExceptionWhenSocketIsNotConnected() { var connectionInfo = new ConnectionInfo("localhost", 6767, Resources.USERNAME, new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); var session = new Session(connectionInfo, _serviceFactoryMock.Object); try { session.Connect(); Assert.Fail(); } catch (SocketException) { session.Disconnect(); } } [TestMethod] public void DisconnectShouldNotThrowExceptionWhenConnectHasNotBeenInvoked() { var connectionInfo = new ConnectionInfo("localhost", 6767, Resources.USERNAME, new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); var session = new Session(connectionInfo, _serviceFactoryMock.Object); session.Disconnect(); } [TestMethod] public void DisposeShouldNotThrowExceptionWhenSocketIsNotConnected() { var connectionInfo = new ConnectionInfo("localhost", 6767, Resources.USERNAME, new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); var session = new Session(connectionInfo, _serviceFactoryMock.Object); try { session.Connect(); Assert.Fail(); } catch (SocketException) { session.Dispose(); } } [TestMethod] public void DisposeShouldNotThrowExceptionWhenConenectHasNotBeenInvoked() { var connectionInfo = new ConnectionInfo("localhost", 6767, Resources.USERNAME, new KeyboardInteractiveAuthenticationMethod(Resources.USERNAME)); var session = new Session(connectionInfo, _serviceFactoryMock.Object); session.Disconnect(); } private static ConnectionInfo CreateConnectionInfo(IPEndPoint serverEndPoint, TimeSpan timeout) { var connectionInfo = new ConnectionInfo( serverEndPoint.Address.ToString(), serverEndPoint.Port, "eric", new NoneAuthenticationMethod("eric")); connectionInfo.Timeout = timeout; return connectionInfo; } } }