Преглед на файлове

Move Integration tests (#1173)

* Renci.SshNet.IntegrationTests

* Renci.SshNet.TestTools.OpenSSH

* Move integration tests to main repo

* Move old tests to new integration tests

* Move old integration tests to new integration tests

* Move more tests

* Move authentication tests

* Move SshClientTests

* Fix some tests

* Remove duplicated test

* Poc of ProcessDisruptor

* Rename

* Some fixes

* Remove performance tests

* Small improvements
Wojciech Nagórski преди 2 години
родител
ревизия
9593e87e54
променени са 100 файла, в които са добавени 15506 реда и са изтрити 1052 реда
  1. 1 0
      src/Renci.SshNet.IntegrationTests/.gitignore
  2. 23 0
      src/Renci.SshNet.IntegrationTests/App.config
  3. 84 0
      src/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs
  4. 427 0
      src/Renci.SshNet.IntegrationTests/AuthenticationTests.cs
  5. 32 0
      src/Renci.SshNet.IntegrationTests/Common/ArrayBuilder.cs
  6. 393 0
      src/Renci.SshNet.IntegrationTests/Common/AsyncSocketListener.cs
  7. 31 0
      src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs
  8. 255 0
      src/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs
  9. 415 0
      src/Renci.SshNet.IntegrationTests/ConnectivityTests.cs
  10. 14 0
      src/Renci.SshNet.IntegrationTests/Credential.cs
  11. 115 0
      src/Renci.SshNet.IntegrationTests/HostConfig.cs
  12. 105 0
      src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs
  13. 23 0
      src/Renci.SshNet.IntegrationTests/HostKeyFile.cs
  14. 10 0
      src/Renci.SshNet.IntegrationTests/IConnectionInfoFactory.cs
  15. 206 0
      src/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs
  16. 36 0
      src/Renci.SshNet.IntegrationTests/LinuxAdminConnectionFactory.cs
  17. 62 0
      src/Renci.SshNet.IntegrationTests/LinuxVMConnectionFactory.cs
  18. 147 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/AesCipherTests.cs
  19. 158 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ForwardedPortLocalTest.cs
  20. 67 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/HMacTest.cs
  21. 336 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ScpClientTest.cs
  22. 10 18
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ChangeDirectory.cs
  23. 72 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.CreateDirectory.cs
  24. 71 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs
  25. 10 25
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs
  26. 263 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs
  27. 6 13
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFile.cs
  28. 5 13
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFileAsync.cs
  29. 8 14
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.SynchronizeDirectories.cs
  30. 17 27
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs
  31. 68 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.cs
  32. 120 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpFileTest.cs
  33. 545 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs
  34. 50 0
      src/Renci.SshNet.IntegrationTests/OldIntegrationTests/TripleDesCipherTest.cs
  35. 84 0
      src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs
  36. 11 0
      src/Renci.SshNet.IntegrationTests/Program.cs
  37. 246 0
      src/Renci.SshNet.IntegrationTests/RemoteSshd.cs
  38. 20 5
      src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
  39. 1 3
      src/Renci.SshNet.IntegrationTests/ScpClientTests.cs
  40. 2379 0
      src/Renci.SshNet.IntegrationTests/ScpTests.cs
  41. 1 2
      src/Renci.SshNet.IntegrationTests/SftpClientTests.cs
  42. 6234 0
      src/Renci.SshNet.IntegrationTests/SftpTests.cs
  43. 1 3
      src/Renci.SshNet.IntegrationTests/SshClientTests.cs
  44. 41 0
      src/Renci.SshNet.IntegrationTests/SshConnectionDisruptor.cs
  45. 36 0
      src/Renci.SshNet.IntegrationTests/SshConnectionRestorer.cs
  46. 972 0
      src/Renci.SshNet.IntegrationTests/SshTests.cs
  47. 80 0
      src/Renci.SshNet.IntegrationTests/TestBase.cs
  48. 1 1
      src/Renci.SshNet.IntegrationTests/TestInitializer.cs
  49. 6 6
      src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs
  50. 21 2
      src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs
  51. 1 1
      src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs
  52. 8 0
      src/Renci.SshNet.IntegrationTests/Users.cs
  53. 1 5
      src/Renci.SshNet.IntegrationTests/Usings.cs
  54. 12 0
      src/Renci.SshNet.IntegrationTests/resources/client/id_dsa
  55. 17 0
      src/Renci.SshNet.IntegrationTests/resources/client/id_dsa.ppk
  56. 27 0
      src/Renci.SshNet.IntegrationTests/resources/client/id_noaccess.rsa
  57. 27 0
      src/Renci.SshNet.IntegrationTests/resources/client/id_rsa
  58. 1 0
      src/Renci.SshNet.IntegrationTests/resources/client/id_rsa.pub
  59. 28 0
      src/Renci.SshNet.IntegrationTests/resources/client/id_rsa_with_pass
  60. 9 0
      src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh
  61. 1 0
      src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh.pub
  62. 10 0
      src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh
  63. 1 0
      src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh.pub
  64. 12 0
      src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh
  65. 1 0
      src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh.pub
  66. 7 0
      src/Renci.SshNet.IntegrationTests/resources/client/key_ed25519_openssh
  67. BIN
      src/Renci.SshNet.IntegrationTests/resources/issue #70.png
  68. 54 0
      src/Renci.SshNet.TestTools.OpenSSH/Cipher.cs
  69. 20 0
      src/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs
  70. 12 0
      src/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs
  71. 10 0
      src/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs
  72. 54 0
      src/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs
  73. 10 0
      src/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs
  74. 53 0
      src/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs
  75. 52 0
      src/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs
  76. 15 0
      src/Renci.SshNet.TestTools.OpenSSH/LogLevel.cs
  77. 57 0
      src/Renci.SshNet.TestTools.OpenSSH/Match.cs
  78. 56 0
      src/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs
  79. 59 0
      src/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs
  80. 21 0
      src/Renci.SshNet.TestTools.OpenSSH/Renci.SshNet.TestTools.OpenSSH.csproj
  81. 501 0
      src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
  82. 41 0
      src/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs
  83. 1 1
      src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs
  84. 1 1
      src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs
  85. 0 1
      src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingHttpContent.cs
  86. 0 1
      src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingStatusLine.cs
  87. 1 1
      src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_Comments.cs
  88. 1 1
      src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_NoComments.cs
  89. 0 1
      src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_TerminatedByLineFeedWithoutCarriageReturn.cs
  90. 0 1
      src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionSucceeded.cs
  91. 0 1
      src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingDestinationAddress.cs
  92. 0 1
      src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyCode.cs
  93. 0 1
      src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyVersion.cs
  94. 0 222
      src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest.cs
  95. 0 103
      src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest.cs
  96. 2 38
      src/Renci.SshNet.Tests/Classes/KeyboardInteractiveConnectionInfoTest.cs
  97. 1 28
      src/Renci.SshNet.Tests/Classes/PasswordAuthenticationMethodTest.cs
  98. 1 133
      src/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs
  99. 1 51
      src/Renci.SshNet.Tests/Classes/PrivateKeyConnectionInfoTest.cs
  100. 0 328
      src/Renci.SshNet.Tests/Classes/ScpClientTest.cs

+ 1 - 0
src/Renci.SshNet.IntegrationTests/.gitignore

@@ -0,0 +1 @@
+TestResults/

+ 23 - 0
src/Renci.SshNet.IntegrationTests/App.config

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <system.diagnostics>
+    <trace autoflush="true"/>
+    <sources>
+      <source name="SshNet.Logging" switchName="SshNetSwitch" switchType="System.Diagnostics.SourceSwitch">
+        <listeners>
+          <!--<add name="SshDotNetTraceFile" />-->
+          <!--<add name="Console"/>-->
+        </listeners>
+      </source>
+    </sources>
+    <switches>
+      <add name="SshNetSwitch" value="Verbose"/>
+    </switches>
+    <sharedListeners>
+      <add name="SshDotNetTraceFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="SshNetTrace.log">
+        <!--<filter type="System.Diagnostics.EventTypeFilter" initializeData="Warning" />-->
+      </add>
+      <add name="Console" type="System.Diagnostics.ConsoleTraceListener" traceOutputOptions="DateTime,Timestamp,ThreadId"/>
+    </sharedListeners>
+  </system.diagnostics>
+</configuration>

+ 84 - 0
src/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs

@@ -0,0 +1,84 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    public class AuthenticationMethodFactory
+    {
+        public PasswordAuthenticationMethod CreatePowerUserPasswordAuthenticationMethod()
+        {
+            var user = Users.Admin;
+            return new PasswordAuthenticationMethod(user.UserName, user.Password);
+        }
+
+        public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyAuthenticationMethod()
+        {
+            var privateKeyFile = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa");
+            return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile);
+        }
+
+        public PrivateKeyAuthenticationMethod CreateRegularUserMultiplePrivateKeyAuthenticationMethod()
+        {
+            var privateKeyFile1 = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa");
+            var privateKeyFile2 = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa");
+            return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile1, privateKeyFile2);
+        }
+
+        public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyWithPassPhraseAuthenticationMethod()
+        {
+            var privateKeyFile = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa_with_pass", "tester");
+            return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile);
+        }
+
+        public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyWithEmptyPassPhraseAuthenticationMethod()
+        {
+            var privateKeyFile = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_rsa_with_pass", null);
+            return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile);
+        }
+
+        public PrivateKeyAuthenticationMethod CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey()
+        {
+            var privateKeyFile = GetPrivateKey("Renci.SshNet.IntegrationTests.resources.client.id_noaccess.rsa");
+            return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKeyFile);
+        }
+
+        public PasswordAuthenticationMethod CreateRegulatUserPasswordAuthenticationMethod()
+        {
+            return new PasswordAuthenticationMethod(Users.Regular.UserName, Users.Regular.Password);
+        }
+
+        public PasswordAuthenticationMethod CreateRegularUserPasswordAuthenticationMethodWithBadPassword()
+        {
+            return new PasswordAuthenticationMethod(Users.Regular.UserName, "xxx");
+        }
+
+        public KeyboardInteractiveAuthenticationMethod CreateRegularUserKeyboardInteractiveAuthenticationMethod()
+        {
+            var keyboardInteractive = new KeyboardInteractiveAuthenticationMethod(Users.Regular.UserName);
+            keyboardInteractive.AuthenticationPrompt += (sender, args) =>
+                {
+                    foreach (var authenticationPrompt in args.Prompts)
+                    {
+                        authenticationPrompt.Response = Users.Regular.Password;
+                    }
+                };
+            return keyboardInteractive;
+        }
+
+        private PrivateKeyFile GetPrivateKey(string resourceName, string passPhrase = null)
+        {
+            using (var stream = GetResourceStream(resourceName))
+            {
+                return new PrivateKeyFile(stream, passPhrase);
+            }
+        }
+
+        private Stream GetResourceStream(string resourceName)
+        {
+            var type = GetType();
+            var resourceStream = type.Assembly.GetManifestResourceStream(resourceName);
+            if (resourceStream == null)
+            {
+                throw new ArgumentException($"Resource '{resourceName}' not found in assembly '{type.Assembly.FullName}'.", nameof(resourceName));
+            }
+            return resourceStream;
+        }
+    }
+}

+ 427 - 0
src/Renci.SshNet.IntegrationTests/AuthenticationTests.cs

@@ -0,0 +1,427 @@
+using Renci.SshNet.Common;
+using Renci.SshNet.IntegrationTests.Common;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    [TestClass]
+    public class AuthenticationTests : IntegrationTestBase
+    {
+        private AuthenticationMethodFactory _authenticationMethodFactory;
+        private IConnectionInfoFactory _connectionInfoFactory;
+        private IConnectionInfoFactory _adminConnectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _authenticationMethodFactory = new AuthenticationMethodFactory();
+            _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort, _authenticationMethodFactory);
+            _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort);
+            _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig();
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+
+            using (var client = new SshClient(_adminConnectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                // Reset the password back to the "regular" password.
+                using (var cmd = client.RunCommand($"echo \"{Users.Regular.Password}\n{Users.Regular.Password}\" | sudo passwd " + Users.Regular.UserName))
+                {
+                    Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
+                }
+
+                // Remove password expiration
+                using (var cmd = client.RunCommand($"sudo chage --expiredate -1 " + Users.Regular.UserName))
+                {
+                    Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_PublicKey()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Authentication")]
+        public void Multifactor_PublicKey_Connect_Then_Reconnect()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_PublicKeyWithPassPhrase()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyWithPassPhraseAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        [ExpectedException(typeof(SshPassPhraseNullOrEmptyException))]
+        public void Multifactor_PublicKeyWithEmptyPassPhrase()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyWithEmptyPassPhraseAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_PublicKey_MultiplePrivateKey()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserMultiplePrivateKeyAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_PublicKey_MultipleAuthenticationMethod()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "publickey")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod(),
+                                                               _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_KeyboardInteractiveAndPublicKey()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive,publickey")
+                             .WithChallengeResponseAuthentication(true)
+                             .WithKeyboardInteractiveAuthentication(true)
+                             .WithUsePAM(true)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(),
+                                                               _authenticationMethodFactory.CreateRegularUserKeyboardInteractiveAuthenticationMethod(),
+                                                               _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_Password_ExceedsPartialSuccessLimit()
+        {
+            // configure server to require more successfull authentications from a given method than our partial
+            // success limit (5) allows
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password,password,password,password,password")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                try
+                {
+                    client.Connect();
+                    Assert.Fail();
+                }
+                catch (SshAuthenticationException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Reached authentication attempt limit for method (password).", ex.Message);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_Password_MatchPartialSuccessLimit()
+        {
+            // configure server to require a number of successfull authentications from a given method that exactly
+            // matches our partial success limit (5)
+
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password,password,password,password")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_Password_Or_PublicKeyAndKeyboardInteractive()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password publickey,keyboard-interactive")
+                             .WithChallengeResponseAuthentication(true)
+                             .WithKeyboardInteractiveAuthentication(true)
+                             .WithUsePAM(true)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod(),
+                                                               _authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_Password_Or_PublicKeyAndPassword_BadPassword()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password publickey,password")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(),
+                                                               _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                try
+                {
+                    client.Connect();
+                    Assert.Fail();
+                }
+                catch (SshAuthenticationException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Permission denied (password).", ex.Message);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Multifactor_PasswordAndPublicKey_Or_PasswordAndPassword()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,publickey password,password")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(),
+                                                               _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+
+            connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserPasswordAuthenticationMethodWithBadPassword(),
+                                                               _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                try
+                {
+                    client.Connect();
+                    Assert.Fail();
+                }
+                catch (SshAuthenticationException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Permission denied (password).", ex.Message);
+                }
+            }
+
+        }
+
+        [TestMethod]
+        public void Multifactor_PasswordAndPassword_Or_PublicKey()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password,password publickey")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(),
+                                                               _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+
+            connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+
+        }
+
+        [TestMethod]
+        public void Multifactor_Password_Or_Password()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "password password")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+
+            connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegulatUserPasswordAuthenticationMethod(),
+                                                           _authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethodWithBadKey());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void KeyboardInteractive_PasswordExpired()
+        {
+            var temporaryPassword = new Random().Next().ToString();
+
+            using (var client = new SshClient(_adminConnectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                // Temporarity modify password so that when we expire this password, we change reset the password back to
+                // the "regular" password.
+                using (var cmd = client.RunCommand($"echo \"{temporaryPassword}\n{temporaryPassword}\" | sudo passwd " + Users.Regular.UserName))
+                {
+                    Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
+                }
+
+                // Force the password to expire immediately
+                using (var cmd = client.RunCommand($"sudo chage -d 0 " + Users.Regular.UserName))
+                {
+                    Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
+                }
+            }
+
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive")
+                             .WithChallengeResponseAuthentication(true)
+                             .WithKeyboardInteractiveAuthentication(true)
+                             .WithUsePAM(true)
+                             .Update()
+                             .Restart();
+
+            var keyboardInteractive = new KeyboardInteractiveAuthenticationMethod(Users.Regular.UserName);
+            int authenticationPromptCount = 0;
+            keyboardInteractive.AuthenticationPrompt += (sender, args) =>
+            {
+                Console.WriteLine(args.Instruction);
+                foreach (var authenticationPrompt in args.Prompts)
+                {
+                    Console.WriteLine(authenticationPrompt.Request);
+                    switch (authenticationPromptCount)
+                    {
+                        case 0:
+                            // Regular password prompt
+                            authenticationPrompt.Response = temporaryPassword;
+                            break;
+                        case 1:
+                            // Password expired, provide current password
+                            authenticationPrompt.Response = temporaryPassword;
+                            break;
+                        case 2:
+                            // Password expired, provide new password
+                            authenticationPrompt.Response = Users.Regular.Password;
+                            break;
+                        case 3:
+                            // Password expired, retype new password
+                            authenticationPrompt.Response = Users.Regular.Password;
+                            break;
+                    }
+
+                    authenticationPromptCount++;
+                }
+            };
+
+            var connectionInfo = _connectionInfoFactory.Create(keyboardInteractive);
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+                Assert.AreEqual(4, authenticationPromptCount);
+            }
+        }
+
+        [TestMethod]
+        public void KeyboardInteractiveConnectionInfo()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive")
+                             .WithChallengeResponseAuthentication(true)
+                             .WithKeyboardInteractiveAuthentication(true)
+                             .WithUsePAM(true)
+                             .Update()
+                             .Restart();
+
+            var host = SshServerHostName;
+            var port = SshServerPort;
+            var username = User.UserName;
+            var password = User.Password;
+
+            #region Example KeyboardInteractiveConnectionInfo AuthenticationPrompt
+
+            var connectionInfo = new KeyboardInteractiveConnectionInfo(host, port, username);
+            connectionInfo.AuthenticationPrompt += delegate (object sender, AuthenticationPromptEventArgs e)
+                                                       {
+                                                           Console.WriteLine(e.Instruction);
+
+                                                           foreach (var prompt in e.Prompts)
+                                                           {
+                                                               Console.WriteLine(prompt.Request);
+                                                               prompt.Response = password;
+                                                           }
+                                                       };
+
+            using (var client = new SftpClient(connectionInfo))
+            {
+                client.Connect();
+
+                //  Do something here
+                client.Disconnect();
+            }
+
+            #endregion
+
+            Assert.AreEqual(connectionInfo.Host, SshServerHostName);
+            Assert.AreEqual(connectionInfo.Username, User.UserName);
+        }
+    }
+}

+ 32 - 0
src/Renci.SshNet.IntegrationTests/Common/ArrayBuilder.cs

@@ -0,0 +1,32 @@
+namespace Renci.SshNet.IntegrationTests.Common
+{
+    public class ArrayBuilder<T>
+    {
+        private readonly List<T> _buffer;
+
+        public ArrayBuilder()
+        {
+            _buffer = new List<T>();
+        }
+
+        public ArrayBuilder<T> Add(T[] array)
+        {
+            return Add(array, 0, array.Length);
+        }
+
+        public ArrayBuilder<T> Add(T[] array, int index, int length)
+        {
+            for (var i = 0; i < length; i++)
+            {
+                _buffer.Add(array[index + i]);
+            }
+
+            return this;
+        }
+
+        public T[] Build()
+        {
+            return _buffer.ToArray();
+        }
+    }
+}

+ 393 - 0
src/Renci.SshNet.IntegrationTests/Common/AsyncSocketListener.cs

@@ -0,0 +1,393 @@
+using System.Net;
+using System.Net.Sockets;
+
+namespace Renci.SshNet.IntegrationTests.Common
+{
+    public class AsyncSocketListener : IDisposable
+    {
+        private readonly IPEndPoint _endPoint;
+        private readonly ManualResetEvent _acceptCallbackDone;
+        private readonly List<Socket> _connectedClients;
+        private readonly object _syncLock;
+        private Socket _listener;
+        private Thread _receiveThread;
+        private bool _started;
+        private string _stackTrace;
+
+        public delegate void BytesReceivedHandler(byte[] bytesReceived, Socket socket);
+        public delegate void ConnectedHandler(Socket socket);
+
+        public event BytesReceivedHandler BytesReceived;
+        public event ConnectedHandler Connected;
+        public event ConnectedHandler Disconnected;
+
+        public AsyncSocketListener(IPEndPoint endPoint)
+        {
+            _endPoint = endPoint;
+            _acceptCallbackDone = new ManualResetEvent(false);
+            _connectedClients = new List<Socket>();
+            _syncLock = new object();
+            ShutdownRemoteCommunicationSocket = true;
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether the <see cref="Socket.Shutdown(SocketShutdown)"/> is invoked on the <see cref="Socket"/>
+        /// that is used to handle the communication with the remote host, when the remote host has closed the connection.
+        /// </summary>
+        /// <value>
+        /// <see langword="true"/> to invoke <see cref="Socket.Shutdown(SocketShutdown)"/> on the <see cref="Socket"/> that is used
+        /// to handle the communication with the remote host, when the remote host has closed the connection; otherwise,
+        /// <see langword="false"/>. The default is <see langword="true"/>.
+        /// </value>
+        public bool ShutdownRemoteCommunicationSocket { get; set; }
+
+        public void Start()
+        {
+            _listener = new Socket(_endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+            _listener.Bind(_endPoint);
+            _listener.Listen(1);
+
+            _started = true;
+
+            _receiveThread = new Thread(StartListener);
+            _receiveThread.Start(_listener);
+
+            _stackTrace = Environment.StackTrace;
+        }
+
+        public void Stop()
+        {
+            _started = false;
+
+            lock (_syncLock)
+            {
+                foreach (var connectedClient in _connectedClients)
+                {
+                    try
+                    {
+                        connectedClient.Shutdown(SocketShutdown.Send);
+                    }
+                    catch (Exception ex)
+                    {
+                        Console.Error.WriteLine("[{0}] Failure shutting down socket: {1}",
+                                                typeof(AsyncSocketListener).FullName,
+                                                ex);
+                    }
+
+                    DrainSocket(connectedClient);
+                    connectedClient.Dispose();
+                }
+
+                _connectedClients.Clear();
+            }
+
+            _listener?.Dispose();
+
+            if (_receiveThread != null)
+            {
+                _receiveThread.Join();
+                _receiveThread = null;
+            }
+        }
+
+        public void Dispose()
+        {
+            Stop();
+            GC.SuppressFinalize(this);
+        }
+
+        private void StartListener(object state)
+        {
+            try
+            {
+                var listener = (Socket) state;
+                while (_started)
+                {
+                    _ = _acceptCallbackDone.Reset();
+                    _ = listener.BeginAccept(AcceptCallback, listener);
+                    _ = _acceptCallbackDone.WaitOne();
+                }
+            }
+            catch (Exception ex)
+            {
+                // On .NET framework when Thread throws an exception then unit tests
+                // were executed without any problem.
+                // On new .NET exceptions from Thread breaks unit tests session.
+                Console.Error.WriteLine("[{0}] Failure in StartListener: {1}",
+                    typeof(AsyncSocketListener).FullName,
+                    ex);
+            }
+        }
+
+        private void AcceptCallback(IAsyncResult ar)
+        {
+            // Signal the main thread to continue
+            _ = _acceptCallbackDone.Set();
+
+            // Get the socket that listens for inbound connections
+            var listener = (Socket) ar.AsyncState;
+
+            // Get the socket that handles the client request
+            Socket handler;
+
+            try
+            {
+                handler = listener.EndAccept(ar);
+            }
+            catch (SocketException ex)
+            {
+                // The listener is stopped through a Dispose() call, which in turn causes
+                // Socket.EndAccept(...) to throw a SocketException or
+                // ObjectDisposedException
+                //
+                // Since we consider such an exception normal when the listener is being
+                // stopped, we only write a message to stderr if the listener is considered
+                // to be up and running
+                if (_started)
+                {
+                    Console.Error.WriteLine("[{0}] Failure accepting new connection: {1}",
+                        typeof(AsyncSocketListener).FullName,
+                        ex);
+                }
+                return;
+            }
+            catch (ObjectDisposedException ex)
+            {
+                // The listener is stopped through a Dispose() call, which in turn causes
+                // Socket.EndAccept(IAsyncResult) to throw a SocketException or
+                // ObjectDisposedException
+                //
+                // Since we consider such an exception normal when the listener is being
+                // stopped, we only write a message to stderr if the listener is considered
+                // to be up and running
+                if (_started)
+                {
+                    Console.Error.WriteLine("[{0}] Failure accepting new connection: {1}",
+                        typeof(AsyncSocketListener).FullName,
+                        ex);
+                }
+                return;
+            }
+
+            // Signal new connection
+            SignalConnected(handler);
+
+            lock (_syncLock)
+            {
+                // Register client socket
+                _connectedClients.Add(handler);
+            }
+
+            var state = new SocketStateObject(handler);
+
+            try
+            {
+                _ = handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state);
+            }
+            catch (SocketException ex)
+            {
+                // The listener is stopped through a Dispose() call, which in turn causes
+                // Socket.BeginReceive(...) to throw a SocketException or
+                // ObjectDisposedException
+                //
+                // Since we consider such an exception normal when the listener is being
+                // stopped, we only write a message to stderr if the listener is considered
+                // to be up and running
+                if (_started)
+                {
+                    Console.Error.WriteLine("[{0}] Failure receiving new data: {1}",
+                        typeof(AsyncSocketListener).FullName,
+                        ex);
+                }
+            }
+            catch (ObjectDisposedException ex)
+            {
+                // The listener is stopped through a Dispose() call, which in turn causes
+                // Socket.BeginReceive(...) to throw a SocketException or
+                // ObjectDisposedException
+                //
+                // Since we consider such an exception normal when the listener is being
+                // stopped, we only write a message to stderr if the listener is considered
+                // to be up and running
+                if (_started)
+                {
+                    Console.Error.WriteLine("[{0}] Failure receiving new data: {1}",
+                                            typeof(AsyncSocketListener).FullName,
+                                            ex);
+                }
+            }
+        }
+
+        private void ReadCallback(IAsyncResult ar)
+        {
+            // Retrieve the state object and the handler socket
+            // from the asynchronous state object
+            var state = (SocketStateObject) ar.AsyncState;
+            var handler = state.Socket;
+
+            int bytesRead;
+            try
+            {
+                // Read data from the client socket.
+                bytesRead = handler.EndReceive(ar, out var errorCode);
+                if (errorCode != SocketError.Success)
+                {
+                    bytesRead = 0;
+                }
+            }
+            catch (SocketException ex)
+            {
+                // The listener is stopped through a Dispose() call, which in turn causes
+                // Socket.EndReceive(...) to throw a SocketException or
+                // ObjectDisposedException
+                //
+                // Since we consider such an exception normal when the listener is being
+                // stopped, we only write a message to stderr if the listener is considered
+                // to be up and running
+                if (_started)
+                {
+                    Console.Error.WriteLine("[{0}] Failure receiving new data: {1}",
+                                            typeof(AsyncSocketListener).FullName,
+                                            ex);
+                }
+                return;
+            }
+            catch (ObjectDisposedException ex)
+            {
+                // The listener is stopped through a Dispose() call, which in turn causes
+                // Socket.EndReceive(...) to throw a SocketException or
+                // ObjectDisposedException
+                //
+                // Since we consider such an exception normal when the listener is being
+                // stopped, we only write a message to stderr if the listener is considered
+                // to be up and running
+                if (_started)
+                {
+                    Console.Error.WriteLine("[{0}] Failure receiving new data: {1}",
+                                            typeof(AsyncSocketListener).FullName,
+                                            ex);
+                }
+                return;
+            }
+
+            void ConnectionDisconnected()
+            {
+                SignalDisconnected(handler);
+
+                if (ShutdownRemoteCommunicationSocket)
+                {
+                    lock (_syncLock)
+                    {
+                        if (!_started)
+                        {
+                            return;
+                        }
+
+                        try
+                        {
+                            handler.Shutdown(SocketShutdown.Send);
+                            handler.Close();
+                        }
+                        catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
+                        {
+                            // On .NET 7 we got Socker Exception with ConnectionReset from Shutdown method
+                            // when the socket is disposed
+                        }
+                        catch (SocketException ex)
+                        {
+                            throw new Exception("Exception in ReadCallback: " + ex.SocketErrorCode + " " + _stackTrace, ex);
+                        }
+                        catch (Exception ex)
+                        {
+                            throw new Exception("Exception in ReadCallback: " + _stackTrace, ex);
+                        }
+
+                        _ = _connectedClients.Remove(handler);
+                    }
+                }
+            }
+
+            if (bytesRead > 0)
+            {
+                var bytesReceived = new byte[bytesRead];
+                Array.Copy(state.Buffer, bytesReceived, bytesRead);
+                SignalBytesReceived(bytesReceived, handler);
+
+                try
+                {
+                    _ = handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state);
+                }
+                catch (ObjectDisposedException)
+                {
+                    // TODO On .NET 7, sometimes we get ObjectDisposedException when _started but only on appveyor, locally it works
+                    ConnectionDisconnected();
+                }
+                catch (SocketException ex)
+                {
+                    if (!_started)
+                    {
+                        throw new Exception("BeginReceive while stopping!", ex);
+                    }
+
+                    throw new Exception("BeginReceive while started!: " + ex.SocketErrorCode + " " + _stackTrace, ex);
+                }
+
+            }
+            else
+            {
+                ConnectionDisconnected();
+            }
+        }
+
+        private void SignalBytesReceived(byte[] bytesReceived, Socket client)
+        {
+            BytesReceived?.Invoke(bytesReceived, client);
+        }
+
+        private void SignalConnected(Socket client)
+        {
+            Connected?.Invoke(client);
+        }
+
+        private void SignalDisconnected(Socket client)
+        {
+            Disconnected?.Invoke(client);
+        }
+
+        private static void DrainSocket(Socket socket)
+        {
+            var buffer = new byte[128];
+
+            try
+            {
+                while (true && socket.Connected)
+                {
+                    var bytesRead = socket.Receive(buffer);
+                    if (bytesRead == 0)
+                    {
+                        break;
+                    }
+                }
+            }
+            catch (SocketException ex)
+            {
+                Console.Error.WriteLine("[{0}] Failure draining socket ({1}): {2}",
+                                        typeof(AsyncSocketListener).FullName,
+                                        ex.SocketErrorCode.ToString("G"),
+                                        ex);
+            }
+        }
+
+        private class SocketStateObject
+        {
+            public Socket Socket { get; private set; }
+
+            public readonly byte[] Buffer = new byte[1024];
+
+            public SocketStateObject(Socket handler)
+            {
+                Socket = handler;
+            }
+        }
+    }
+}

+ 31 - 0
src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs

@@ -0,0 +1,31 @@
+using Renci.SshNet.TestTools.OpenSSH;
+
+namespace Renci.SshNet.IntegrationTests.Common
+{
+    internal static class RemoteSshdConfigExtensions
+    {
+        private const string DefaultAuthenticationMethods = "password publickey";
+
+        public static void Reset(this RemoteSshdConfig remoteSshdConfig)
+        {
+            remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, DefaultAuthenticationMethods)
+                            .WithChallengeResponseAuthentication(false)
+                            .WithKeyboardInteractiveAuthentication(false)
+                            .PrintMotd()
+                            .WithLogLevel(LogLevel.Debug3)
+                            .ClearHostKeyFiles()
+                            .AddHostKeyFile(HostKeyFile.Rsa.FilePath)
+                            .ClearSubsystems()
+                            .AddSubsystem(new Subsystem("sftp", "/usr/lib/ssh/sftp-server"))
+                            .ClearCiphers()
+                            .ClearKeyExchangeAlgorithms()
+                            .ClearHostKeyAlgorithms()
+                            .AddHostKeyAlgorithm(HostKeyAlgorithm.SshRsa)
+                            .ClearPublicKeyAcceptedAlgorithms()
+                            .AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.SshRsa)
+                            .WithUsePAM(true)
+                            .Update()
+                            .Restart();
+        }
+    }
+}

+ 255 - 0
src/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs

@@ -0,0 +1,255 @@
+using System.Net;
+using System.Net.Sockets;
+
+using Renci.SshNet.Abstractions;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Transport;
+
+namespace Renci.SshNet.IntegrationTests.Common
+{
+    class Socks5Handler
+    {
+        private readonly IPEndPoint _proxyEndPoint;
+        private readonly string _userName;
+        private readonly string _password;
+
+        public Socks5Handler(IPEndPoint proxyEndPoint, string userName, string password)
+        {
+            _proxyEndPoint = proxyEndPoint;
+            _userName = userName;
+            _password = password;
+        }
+
+        public Socket Connect(IPEndPoint endPoint)
+        {
+            if (endPoint == null)
+            {
+                throw new ArgumentNullException("endPoint");
+            }
+
+            var addressBytes = GetAddressBytes(endPoint);
+            return Connect(addressBytes, endPoint.Port);
+        }
+
+        public Socket Connect(string host, int port)
+        {
+            if (host == null)
+            {
+                throw new ArgumentNullException(nameof(host));
+            }
+
+            if (host.Length > byte.MaxValue)
+            {
+                throw new ArgumentException($@"Cannot be more than {byte.MaxValue} characters.", nameof(host));
+            }
+
+            var addressBytes = new byte[host.Length + 2];
+            addressBytes[0] = 0x03;
+            addressBytes[1] = (byte) host.Length;
+            Encoding.ASCII.GetBytes(host, 0, host.Length, addressBytes, 2);
+            return Connect(addressBytes, port);
+        }
+
+        private Socket Connect(byte[] addressBytes, int port)
+        {
+            var socket = SocketAbstraction.Connect(_proxyEndPoint, TimeSpan.FromSeconds(5));
+
+            //  Send socks version number
+            SocketWriteByte(socket, 0x05);
+
+            //  Send number of supported authentication methods
+            SocketWriteByte(socket, 0x02);
+
+            //  Send supported authentication methods
+            SocketWriteByte(socket, 0x00); //  No authentication
+            SocketWriteByte(socket, 0x02); //  Username/Password
+
+            var socksVersion = SocketReadByte(socket);
+            if (socksVersion != 0x05)
+            {
+                throw new ProxyException(string.Format("SOCKS Version '{0}' is not supported.", socksVersion));
+            }
+
+            var authenticationMethod = SocketReadByte(socket);
+            switch (authenticationMethod)
+            {
+                case 0x00:
+                    break;
+                case 0x02:
+
+                    //  Send version
+                    SocketWriteByte(socket, 0x01);
+
+                    var username = Encoding.ASCII.GetBytes(_userName);
+                    if (username.Length > byte.MaxValue)
+                    {
+                        throw new ProxyException("Proxy username is too long.");
+                    }
+
+                    //  Send username length
+                    SocketWriteByte(socket, (byte) username.Length);
+
+                    //  Send username
+                    SocketAbstraction.Send(socket, username);
+
+                    var password = Encoding.ASCII.GetBytes(_password);
+
+                    if (password.Length > byte.MaxValue)
+                    {
+                        throw new ProxyException("Proxy password is too long.");
+                    }
+
+                    //  Send username length
+                    SocketWriteByte(socket, (byte) password.Length);
+
+                    //  Send username
+                    SocketAbstraction.Send(socket, password);
+
+                    var serverVersion = SocketReadByte(socket);
+
+                    if (serverVersion != 1)
+                    {
+                        throw new ProxyException("SOCKS5: Server authentication version is not valid.");
+                    }
+
+                    var statusCode = SocketReadByte(socket);
+                    if (statusCode != 0)
+                    {
+                        throw new ProxyException("SOCKS5: Username/Password authentication failed.");
+                    }
+
+                    break;
+                case 0xFF:
+                    throw new ProxyException("SOCKS5: No acceptable authentication methods were offered.");
+                default:
+                    throw new ProxyException("SOCKS5: No acceptable authentication methods were offered.");
+            }
+
+            //  Send socks version number
+            SocketWriteByte(socket, 0x05);
+
+            //  Send command code
+            SocketWriteByte(socket, 0x01); //  establish a TCP/IP stream connection
+
+            //  Send reserved, must be 0x00
+            SocketWriteByte(socket, 0x00);
+
+            //  Send address type and address
+            SocketAbstraction.Send(socket, addressBytes);
+
+            //  Send port
+            SocketWriteByte(socket, (byte)(port / 0xFF));
+            SocketWriteByte(socket, (byte)(port % 0xFF));
+
+            //  Read Server SOCKS5 version
+            if (SocketReadByte(socket) != 5)
+            {
+                throw new ProxyException("SOCKS5: Version 5 is expected.");
+            }
+
+            //  Read response code
+            var status = SocketReadByte(socket);
+
+            switch (status)
+            {
+                case 0x00:
+                    break;
+                case 0x01:
+                    throw new ProxyException("SOCKS5: General failure.");
+                case 0x02:
+                    throw new ProxyException("SOCKS5: Connection not allowed by ruleset.");
+                case 0x03:
+                    throw new ProxyException("SOCKS5: Network unreachable.");
+                case 0x04:
+                    throw new ProxyException("SOCKS5: Host unreachable.");
+                case 0x05:
+                    throw new ProxyException("SOCKS5: Connection refused by destination host.");
+                case 0x06:
+                    throw new ProxyException("SOCKS5: TTL expired.");
+                case 0x07:
+                    throw new ProxyException("SOCKS5: Command not supported or protocol error.");
+                case 0x08:
+                    throw new ProxyException("SOCKS5: Address type not supported.");
+                default:
+                    throw new ProxyException("SOCKS4: Not valid response.");
+            }
+
+            //  Read 0
+            if (SocketReadByte(socket) != 0)
+            {
+                throw new ProxyException("SOCKS5: 0 byte is expected.");
+            }
+
+            var addressType = SocketReadByte(socket);
+            var responseIp = new byte[16];
+
+            switch (addressType)
+            {
+                case 0x01:
+                    SocketRead(socket, responseIp, 0, 4);
+                    break;
+                case 0x04:
+                    SocketRead(socket, responseIp, 0, 16);
+                    break;
+                default:
+                    throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType));
+            }
+
+            var portBytes = new byte[2];
+
+            //  Read 2 bytes to be ignored
+            SocketRead(socket, portBytes, 0, 2);
+
+            return socket;
+        }
+
+        private static byte[] GetAddressBytes(IPEndPoint endPoint)
+        {
+            if (endPoint.AddressFamily == AddressFamily.InterNetwork)
+            {
+                var addressBytes = new byte[4 + 1];
+                addressBytes[0] = 0x01;
+                var address = endPoint.Address.GetAddressBytes();
+                Buffer.BlockCopy(address, 0, addressBytes, 1, address.Length);
+                return addressBytes;
+            }
+
+            if (endPoint.AddressFamily == AddressFamily.InterNetworkV6)
+            {
+                var addressBytes = new byte[16 + 1];
+                addressBytes[0] = 0x04;
+                var address = endPoint.Address.GetAddressBytes();
+                Buffer.BlockCopy(address, 0, addressBytes, 1, address.Length);
+                return addressBytes;
+            }
+
+            throw new ProxyException(string.Format("SOCKS5: IP address '{0}' is not supported.", endPoint.Address));
+        }
+
+        private static void SocketWriteByte(Socket socket, byte data)
+        {
+            SocketAbstraction.Send(socket, new[] { data });
+        }
+
+        private static byte SocketReadByte(Socket socket)
+        {
+            var buffer = new byte[1];
+            SocketRead(socket, buffer, 0, 1);
+            return buffer[0];
+        }
+
+        private static int SocketRead(Socket socket, byte[] buffer, int offset, int length)
+        {
+            var bytesRead = SocketAbstraction.Read(socket, buffer, offset, length, TimeSpan.FromMilliseconds(-1));
+            if (bytesRead == 0)
+            {
+                // when we're in the disconnecting state (either triggered by client or server), then the
+                // SshConnectionException will interrupt the message listener loop (if not already interrupted)
+                // and the exception itself will be ignored (in RaiseError)
+                throw new SshConnectionException("An established connection was aborted by the server.",
+                    DisconnectReason.ConnectionLost);
+            }
+            return bytesRead;
+        }
+    }
+}

+ 415 - 0
src/Renci.SshNet.IntegrationTests/ConnectivityTests.cs

@@ -0,0 +1,415 @@
+using Renci.SshNet.Common;
+using Renci.SshNet.IntegrationTests.Common;
+using Renci.SshNet.Messages.Transport;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    [TestClass]
+    public class ConnectivityTests : IntegrationTestBase
+    {
+        private AuthenticationMethodFactory _authenticationMethodFactory;
+        private IConnectionInfoFactory _connectionInfoFactory;
+        private IConnectionInfoFactory _adminConnectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+        private SshConnectionDisruptor _sshConnectionDisruptor;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _authenticationMethodFactory = new AuthenticationMethodFactory();
+            _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort, _authenticationMethodFactory);
+            _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort);
+            _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig();
+            _sshConnectionDisruptor = new SshConnectionDisruptor(_adminConnectionInfoFactory);
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+        }
+
+        [TestMethod]
+        public void Common_CreateMoreChannelsThanMaxSessions()
+        {
+            var connectionInfo = _connectionInfoFactory.Create();
+            connectionInfo.MaxSessions = 2;
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+
+                // create one more channel than the maximum number of sessions
+                // as that would block indefinitely when creating the last channel
+                // if the channel would not be properly closed
+                for (var i = 0; i < connectionInfo.MaxSessions + 1; i++)
+                {
+                    using (var stream = client.CreateShellStream("vt220", 20, 20, 20, 20, 0))
+                    {
+                        stream.WriteLine("echo test");
+                        stream.ReadLine();
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Common_DisposeAfterLossOfNetworkConnectivity()
+        {
+            var hostNetworkConnectionDisabled = false;
+            SshConnectionRestorer disruptor = null;
+            try
+            {
+                Exception errorOccurred = null;
+                int count = 0;
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.ErrorOccurred += (sender, args) =>
+                                                {
+                                                    Console.WriteLine("Exception " + count++);
+                                                    Console.WriteLine(args.Exception);
+                                                    errorOccurred = args.Exception;
+                                                };
+                    client.Connect();
+
+                    disruptor = _sshConnectionDisruptor.BreakConnections();
+                    hostNetworkConnectionDisabled = true;
+                    WaitForConnectionInterruption(client);
+                }
+                
+                Assert.IsNotNull(errorOccurred);
+                Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType());
+
+                var connectionException = (SshConnectionException) errorOccurred;
+                Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason);
+                Assert.IsNull(connectionException.InnerException);
+                Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message);
+            }
+            finally
+            {
+                if (hostNetworkConnectionDisabled)
+                {
+                    disruptor?.RestoreConnections();
+                    disruptor?.Dispose();
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Common_DetectLossOfNetworkConnectivityThroughKeepAlive()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                Exception errorOccurred = null;
+                int count = 0;
+                client.ErrorOccurred += (sender, args) =>
+                                            {
+                                                Console.WriteLine("Exception "+ count++);
+                                                Console.WriteLine(args.Exception);
+                                                errorOccurred = args.Exception;
+                                            };
+                client.KeepAliveInterval = new TimeSpan(0, 0, 0, 0, 50);
+                client.Connect();
+
+                var disruptor = _sshConnectionDisruptor.BreakConnections();
+
+                try
+                {
+                    WaitForConnectionInterruption(client);
+
+                    Assert.IsFalse(client.IsConnected);
+
+                    Assert.IsNotNull(errorOccurred);
+                    Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType());
+
+                    var connectionException = (SshConnectionException) errorOccurred;
+                    Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason);
+                    Assert.IsNull(connectionException.InnerException);
+                    Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message);
+                }
+                finally
+                {
+                    disruptor?.RestoreConnections();
+                    disruptor?.Dispose();
+                }
+            }
+        }
+
+        private static void WaitForConnectionInterruption(SftpClient client)
+        {
+            for (var i = 0; i < 500; i++)
+            {
+                if (!client.IsConnected)
+                {
+                    break;
+                }
+
+                Thread.Sleep(100);
+            }
+
+            // After interruption, you have to wait for the events to propagate.
+            Thread.Sleep(100);
+        }
+
+        [TestMethod]
+        public void Common_DetectConnectionResetThroughSftpInvocation()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.KeepAliveInterval = TimeSpan.FromSeconds(1);
+                client.OperationTimeout = TimeSpan.FromSeconds(60);
+                ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false);
+                Exception errorOccurred = null;
+                client.ErrorOccurred += (sender, args) =>
+                {
+                    errorOccurred = args.Exception;
+                    errorOccurredSignaled.Set();
+                };
+                client.Connect();
+
+                var disruptor = _sshConnectionDisruptor.BreakConnections();
+
+                try
+                {
+                    client.ListDirectory("/");
+                    Assert.Fail();
+                }
+                catch (SshConnectionException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Client not connected.", ex.Message);
+
+                    Assert.IsNotNull(errorOccurred);
+                    Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType());
+
+                    var connectionException = (SshConnectionException) errorOccurred;
+                    Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason);
+                    Assert.IsNull(connectionException.InnerException);
+                    Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message);
+                }
+                finally
+                {
+                    disruptor.RestoreConnections();
+                    disruptor.Dispose();
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Common_LossOfNetworkConnectivityDisconnectAndConnect()
+        {
+            bool vmNetworkConnectionDisabled = false;
+            SshConnectionRestorer disruptor = null;
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    Exception errorOccurred = null;
+                    client.ErrorOccurred += (sender, args) => errorOccurred = args.Exception;
+
+                    client.Connect();
+
+                    disruptor = _sshConnectionDisruptor.BreakConnections();
+                    vmNetworkConnectionDisabled = true;
+
+                    WaitForConnectionInterruption(client);
+                    // disconnect while network connectivity is lost
+                    client.Disconnect();
+
+                    Assert.IsFalse(client.IsConnected);
+
+                    disruptor.RestoreConnections();
+                    vmNetworkConnectionDisabled = false;
+
+                    // connect when network connectivity is restored
+                    client.Connect();
+                    client.ChangeDirectory(client.WorkingDirectory);
+                    client.Dispose();
+
+                    Assert.IsNotNull(errorOccurred);
+                    Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType());
+
+                    var connectionException = (SshConnectionException) errorOccurred;
+                    Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason);
+                    Assert.IsNull(connectionException.InnerException);
+                    Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message);
+                }
+            }
+            finally
+            {
+                if (vmNetworkConnectionDisabled)
+                {
+                    disruptor.RestoreConnections();
+                }
+                disruptor?.Dispose();
+            }
+        }
+
+        [TestMethod]
+        public void Common_DetectLossOfNetworkConnectivityThroughSftpInvocation()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false);
+                Exception errorOccurred = null;
+                client.ErrorOccurred += (sender, args) =>
+                {
+                    errorOccurred = args.Exception;
+                    errorOccurredSignaled.Set();
+                };
+                client.Connect();
+
+                var disruptor = _sshConnectionDisruptor.BreakConnections();
+                Thread.Sleep(100);
+                try
+                {
+                    client.ListDirectory("/");
+                    Assert.Fail();
+                }
+                catch (SshConnectionException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Client not connected.", ex.Message);
+
+                    Assert.IsNotNull(errorOccurred);
+                    Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType());
+
+                    var connectionException = (SshConnectionException) errorOccurred;
+                    Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason);
+                    Assert.IsNull(connectionException.InnerException);
+                    Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message);
+                }
+                finally
+                {
+                    disruptor.RestoreConnections();
+                    disruptor.Dispose();
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Common_DetectSessionKilledOnServer()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false);
+                Exception errorOccurred = null;
+                client.ErrorOccurred += (sender, args) =>
+                {
+                    errorOccurred = args.Exception;
+                    errorOccurredSignaled.Set();
+                };
+                client.Connect();
+
+                // Kill the server session
+                using (var adminClient = new SshClient(_adminConnectionInfoFactory.Create()))
+                {
+                    adminClient.Connect();
+
+                    var command = $"sudo ps --no-headers -u {client.ConnectionInfo.Username} -f | grep \"{client.ConnectionInfo.Username}@notty\" | awk '{{print $2}}' | xargs sudo kill -9";
+                    var sshCommand = adminClient.CreateCommand(command);
+                    var result = sshCommand.Execute();
+                    Assert.AreEqual(0, sshCommand.ExitStatus, sshCommand.Error);
+                }
+
+                Assert.IsTrue(errorOccurredSignaled.WaitOne(200));
+                Assert.IsNotNull(errorOccurred);
+                Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType());
+                Assert.IsNull(errorOccurred.InnerException);
+                Assert.AreEqual("An established connection was aborted by the server.", errorOccurred.Message);
+                Assert.IsFalse(client.IsConnected);
+            }
+        }
+
+        [TestMethod]
+        public void Common_HostKeyValidation_Failure()
+        {
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.HostKeyReceived += (sender, e) => { e.CanTrust = false; };
+
+                try
+                {
+                    client.Connect();
+                    Assert.Fail();
+                }
+                catch (SshConnectionException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Key exchange negotiation failed.", ex.Message);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Common_HostKeyValidation_Success()
+        {
+            byte[] host_rsa_key_openssh_fingerprint =
+                {
+                    0x3d, 0x90, 0xd8, 0x0d, 0xd5, 0xe0, 0xb6, 0x13,
+                    0x42, 0x7c, 0x78, 0x1e, 0x19, 0xa3, 0x99, 0x2b
+                };
+
+            var hostValidationSuccessful = false;
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.HostKeyReceived += (sender, e) =>
+                {
+                    if (host_rsa_key_openssh_fingerprint.Length == e.FingerPrint.Length)
+                    {
+                        for (var i = 0; i < host_rsa_key_openssh_fingerprint.Length; i++)
+                        {
+                            if (host_rsa_key_openssh_fingerprint[i] != e.FingerPrint[i])
+                            {
+                                e.CanTrust = false;
+                                break;
+                            }
+                        }
+
+                        hostValidationSuccessful = e.CanTrust;
+                    }
+                    else
+                    {
+                        e.CanTrust = false;
+                    }
+                };
+                client.Connect();
+            }
+
+            Assert.IsTrue(hostValidationSuccessful);
+        }
+
+        /// <summary>
+        /// Verifies whether we handle a disconnect initiated by the SSH server (through a SSH_MSG_DISCONNECT message).
+        /// </summary>
+        /// <remarks>
+        /// We force this by only configuring <c>keyboard-interactive</c> as authentication method, while <c>ChallengeResponseAuthentication</c>
+        /// is not enabled.  This causes OpenSSH to terminate the connection because there are no authentication methods left.
+        /// </remarks>
+        [TestMethod]
+        public void Common_ServerRejectsConnection()
+        {
+            _remoteSshdConfig.WithAuthenticationMethods(Users.Regular.UserName, "keyboard-interactive")
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(_authenticationMethodFactory.CreateRegularUserKeyboardInteractiveAuthenticationMethod());
+            using (var client = new SftpClient(connectionInfo))
+            {
+                try
+                {
+                    client.Connect();
+                    Assert.Fail();
+                }
+                catch (SshConnectionException ex)
+                {
+                    Assert.AreEqual(DisconnectReason.ProtocolError, ex.DisconnectReason);
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("The connection was closed by the server: no authentication methods enabled (ProtocolError).", ex.Message);
+                }
+            }
+        }
+
+    }
+}

+ 14 - 0
src/Renci.SshNet.IntegrationTests/Credential.cs

@@ -0,0 +1,14 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    internal class Credential
+    {
+        public Credential(string userName, string password)
+        {
+            UserName = userName;
+            Password = password;
+        }
+
+        public string UserName { get; }
+        public string Password { get; }
+    }
+}

+ 115 - 0
src/Renci.SshNet.IntegrationTests/HostConfig.cs

@@ -0,0 +1,115 @@
+using System.Net;
+using System.Text.RegularExpressions;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    class HostConfig
+    {
+        private static readonly Regex HostsEntryRegEx = new Regex(@"^(?<IPAddress>[\S]+)\s+(?<HostName>[a-zA-Z]+[a-zA-Z\-\.]*[a-zA-Z]+)\s*(?<Aliases>.+)*$", RegexOptions.Singleline);
+
+        public List<HostEntry> Entries { get; }
+
+        private HostConfig()
+        {
+            Entries = new List<HostEntry>();
+        }
+
+        public static HostConfig Read(ScpClient scpClient, string path)
+        {
+            HostConfig hostConfig = new HostConfig();
+
+            using (var ms = new MemoryStream())
+            {
+                scpClient.Download(path, ms);
+                ms.Position = 0;
+
+                using (var sr = new StreamReader(ms, Encoding.ASCII))
+                {
+                    string line;
+                    while ((line = sr.ReadLine()) != null)
+                    {
+                        // skip comments
+                        if (line.StartsWith("#"))
+                        {
+                            continue;
+                        }
+
+                        var hostEntryMatch = HostsEntryRegEx.Match(line);
+                        if (!hostEntryMatch.Success)
+                        {
+                            continue;
+                        }
+
+                        var entryIPAddress = hostEntryMatch.Groups["IPAddress"].Value;
+                        var entryAliasesGroup = hostEntryMatch.Groups["Aliases"];
+
+                        var entry = new HostEntry(IPAddress.Parse(entryIPAddress), hostEntryMatch.Groups["HostName"].Value);
+
+                        if (entryAliasesGroup.Success)
+                        {
+                            var aliases = entryAliasesGroup.Value.Split(' ');
+                            foreach (var alias in aliases)
+                            {
+                                entry.Aliases.Add(alias);
+                            }
+                        }
+
+                        hostConfig.Entries.Add(entry);
+                    }
+                }
+            }
+
+            return hostConfig;
+        }
+
+        public void Write(ScpClient scpClient, string path)
+        {
+            using (var ms = new MemoryStream())
+            using (var sw = new StreamWriter(ms, Encoding.ASCII))
+            {
+                // Use linux line ending
+                sw.NewLine = "\n";
+                     
+                foreach (var hostEntry in Entries)
+                {
+                    sw.Write(hostEntry.IPAddress);
+                    sw.Write("    ");
+                    sw.Write(hostEntry.HostName);
+
+                    if (hostEntry.Aliases.Count > 0)
+                    {
+                        sw.Write("    ");
+                        for (var i = 0; i < hostEntry.Aliases.Count; i++)
+                        {
+                            if (i > 0)
+                            {
+                                sw.Write(' ');
+                            }
+                            sw.Write(hostEntry.Aliases[i]);
+                        }
+                    }
+                    sw.WriteLine();
+                }
+
+                sw.Flush();
+                ms.Position = 0;
+
+                scpClient.Upload(ms, path);
+            }
+        }
+    }
+
+    public class HostEntry
+    {
+        public HostEntry(IPAddress ipAddress, string hostName)
+        {
+            IPAddress = ipAddress;
+            HostName = hostName;
+            Aliases = new List<string>();
+        }
+
+        public IPAddress IPAddress { get; private set; }
+        public string HostName { get; set; }
+        public List<string> Aliases { get; }
+    }
+}

+ 105 - 0
src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs

@@ -0,0 +1,105 @@
+using Renci.SshNet.Common;
+using Renci.SshNet.IntegrationTests.Common;
+using Renci.SshNet.TestTools.OpenSSH;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    [TestClass]
+    public class HostKeyAlgorithmTests : IntegrationTestBase
+    {
+        private IConnectionInfoFactory _connectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort);
+            _remoteSshdConfig = new RemoteSshd(new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort)).OpenConfig();
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+        }
+        
+        [TestMethod]
+        [Ignore] // No longer supported in recent versions of OpenSSH
+        public void SshDsa()
+        {
+            _remoteSshdConfig.ClearHostKeyAlgorithms()
+                             .AddHostKeyAlgorithm(HostKeyAlgorithm.SshDsa)
+                             .ClearHostKeyFiles()
+                             .AddHostKeyFile(HostKeyFile.Dsa.FilePath)
+                             .Update()
+                             .Restart();
+
+            HostKeyEventArgs hostKeyEventsArgs = null;
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.HostKeyReceived += (sender, e) => hostKeyEventsArgs = e;
+                client.Connect();
+                client.Disconnect();
+            }
+
+            Assert.IsNotNull(hostKeyEventsArgs);
+            Assert.AreEqual(HostKeyFile.Dsa.KeyName, hostKeyEventsArgs.HostKeyName);
+            Assert.AreEqual(1024, hostKeyEventsArgs.KeyLength);
+            Assert.IsTrue(hostKeyEventsArgs.FingerPrint.SequenceEqual(HostKeyFile.Dsa.FingerPrint));
+        }
+
+        [TestMethod]
+        public void SshRsa()
+        {
+            _remoteSshdConfig.ClearHostKeyAlgorithms()
+                             .AddHostKeyAlgorithm(HostKeyAlgorithm.SshRsa)
+                             .Update()
+                             .Restart();
+
+            HostKeyEventArgs hostKeyEventsArgs = null;
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.HostKeyReceived += (sender, e) => hostKeyEventsArgs = e;
+                client.Connect();
+                client.Disconnect();
+            }
+
+            Assert.IsNotNull(hostKeyEventsArgs);
+            Assert.AreEqual(HostKeyFile.Rsa.KeyName, hostKeyEventsArgs.HostKeyName);
+            Assert.AreEqual(3072, hostKeyEventsArgs.KeyLength);
+            Assert.IsTrue(hostKeyEventsArgs.FingerPrint.SequenceEqual(HostKeyFile.Rsa.FingerPrint));
+        }
+
+        [TestMethod]
+        public void SshEd25519()
+        {
+            _remoteSshdConfig.ClearHostKeyAlgorithms()
+                             .AddHostKeyAlgorithm(HostKeyAlgorithm.SshEd25519)
+                             .ClearHostKeyFiles()
+                             .AddHostKeyFile(HostKeyFile.Ed25519.FilePath)
+                             .Update()
+                             .Restart();
+
+            HostKeyEventArgs hostKeyEventsArgs = null;
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.HostKeyReceived += (sender, e) => hostKeyEventsArgs = e;
+                client.Connect();
+                client.Disconnect();
+            }
+
+            Assert.IsNotNull(hostKeyEventsArgs);
+            Assert.AreEqual(HostKeyFile.Ed25519.KeyName, hostKeyEventsArgs.HostKeyName);
+            Assert.AreEqual(256, hostKeyEventsArgs.KeyLength);
+            Assert.IsTrue(hostKeyEventsArgs.FingerPrint.SequenceEqual(HostKeyFile.Ed25519.FingerPrint));
+        }
+
+        private void Client_HostKeyReceived(object sender, HostKeyEventArgs e)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 23 - 0
src/Renci.SshNet.IntegrationTests/HostKeyFile.cs

@@ -0,0 +1,23 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    public sealed class HostKeyFile
+    {
+        public static readonly HostKeyFile Rsa = new HostKeyFile("ssh-rsa", "/etc/ssh/ssh_host_rsa_key", new byte[] { 0x3d, 0x90, 0xd8, 0x0d, 0xd5, 0xe0, 0xb6, 0x13, 0x42, 0x7c, 0x78, 0x1e, 0x19, 0xa3, 0x99, 0x2b });
+        public static readonly HostKeyFile Dsa = new HostKeyFile("ssh-dsa", "/etc/ssh/ssh_host_dsa_key", new byte[] { 0x3d, 0x90, 0xd8, 0x0d, 0xd5, 0xe0, 0xb6, 0x13, 0x42, 0x7c, 0x78, 0x1e, 0x19, 0xa3, 0x99, 0x2b });
+        public static readonly HostKeyFile Ed25519 = new HostKeyFile("ssh-ed25519", "/etc/ssh/ssh_host_ed25519_key", new byte[] { 0xb3, 0xb9, 0xd0, 0x1b, 0x73, 0xc4, 0x60, 0xb4, 0xce, 0xed, 0x06, 0xf8, 0x58, 0x49, 0xa3, 0xda });
+        public const string Ecdsa = "/etc/ssh/ssh_host_ecdsa_key";
+
+        private HostKeyFile(string keyName, string filePath, byte[] fingerPrint)
+        {
+            KeyName = keyName;
+            FilePath = filePath;
+            FingerPrint = fingerPrint;
+        }
+
+        public string KeyName {get; }
+        public string FilePath { get; }
+        public byte[] FingerPrint { get; }
+    }
+
+
+}

+ 10 - 0
src/Renci.SshNet.IntegrationTests/IConnectionInfoFactory.cs

@@ -0,0 +1,10 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    public interface IConnectionInfoFactory
+    {
+        ConnectionInfo Create();
+        ConnectionInfo Create(params AuthenticationMethod[] authenticationMethods);
+        ConnectionInfo CreateWithProxy();
+        ConnectionInfo CreateWithProxy(params AuthenticationMethod[] authenticationMethods);
+    }
+}

+ 206 - 0
src/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs

@@ -0,0 +1,206 @@
+using Renci.SshNet.IntegrationTests.Common;
+using Renci.SshNet.TestTools.OpenSSH;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    [TestClass]
+    public class KeyExchangeAlgorithmTests : IntegrationTestBase
+    {
+        private IConnectionInfoFactory _connectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort);
+            _remoteSshdConfig = new RemoteSshd(new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort)).OpenConfig();
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+        }
+
+        [TestMethod]
+        public void Curve25519Sha256()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.Curve25519Sha256)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Curve25519Sha256Libssh()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.Curve25519Sha256Libssh)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void DiffieHellmanGroup1Sha1()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup1Sha1)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void DiffieHellmanGroup14Sha1()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup14Sha1)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void DiffieHellmanGroup14Sha256()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup14Sha256)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void DiffieHellmanGroup16Sha512()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup16Sha512)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [Ignore]
+        public void DiffieHellmanGroup18Sha512()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroup18Sha512)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void DiffieHellmanGroupExchangeSha1()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroupExchangeSha1)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void DiffieHellmanGroupExchangeSha256()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.DiffieHellmanGroupExchangeSha256)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void EcdhSha2Nistp256()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.EcdhSha2Nistp256)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void EcdhSha2Nistp384()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.EcdhSha2Nistp384)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void EcdhSha2Nistp521()
+        {
+            _remoteSshdConfig.ClearKeyExchangeAlgorithms()
+                             .AddKeyExchangeAlgorithm(KeyExchangeAlgorithm.EcdhSha2Nistp521)
+                             .Update()
+                             .Restart();
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+    }
+}

+ 36 - 0
src/Renci.SshNet.IntegrationTests/LinuxAdminConnectionFactory.cs

@@ -0,0 +1,36 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    public class LinuxAdminConnectionFactory : IConnectionInfoFactory
+    {
+        private readonly string _host;
+        private readonly int _port;
+
+        public LinuxAdminConnectionFactory(string sshServerHostName, ushort sshServerPort)
+        {
+            _host = sshServerHostName;
+            _port = sshServerPort;
+        }
+
+        public ConnectionInfo Create()
+        {
+            var user = Users.Admin;
+            return new ConnectionInfo(_host, _port, user.UserName, new PasswordAuthenticationMethod(user.UserName, user.Password));
+        }
+
+        public ConnectionInfo Create(params AuthenticationMethod[] authenticationMethods)
+        {
+            throw new NotImplementedException();
+        }
+
+        public ConnectionInfo CreateWithProxy()
+        {
+            throw new NotImplementedException();
+        }
+
+        public ConnectionInfo CreateWithProxy(params AuthenticationMethod[] authenticationMethods)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}
+

+ 62 - 0
src/Renci.SshNet.IntegrationTests/LinuxVMConnectionFactory.cs

@@ -0,0 +1,62 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    public class LinuxVMConnectionFactory : IConnectionInfoFactory
+    {
+        
+
+        private const string ProxyHost = "127.0.0.1";
+        private const int ProxyPort = 1234;
+        private const string ProxyUserName = "test";
+        private const string ProxyPassword = "123";
+        private readonly string _host;
+        private readonly int _port;
+        private readonly AuthenticationMethodFactory _authenticationMethodFactory;
+
+
+        public LinuxVMConnectionFactory(string sshServerHostName, ushort sshServerPort)
+        {
+            _host = sshServerHostName;
+            _port = sshServerPort;
+
+            _authenticationMethodFactory = new AuthenticationMethodFactory();
+        }
+
+        public LinuxVMConnectionFactory(string sshServerHostName, ushort sshServerPort, AuthenticationMethodFactory authenticationMethodFactory)
+        {
+            _host = sshServerHostName;
+            _port = sshServerPort;
+
+            _authenticationMethodFactory = authenticationMethodFactory;
+        }
+
+        public ConnectionInfo Create()
+        {
+            return Create(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
+        }
+
+        public ConnectionInfo Create(params AuthenticationMethod[] authenticationMethods)
+        {
+            return new ConnectionInfo(_host, _port, Users.Regular.UserName, authenticationMethods);
+        }
+
+        public ConnectionInfo CreateWithProxy()
+        {
+            return CreateWithProxy(_authenticationMethodFactory.CreateRegularUserPrivateKeyAuthenticationMethod());
+        }
+
+        public ConnectionInfo CreateWithProxy(params AuthenticationMethod[] authenticationMethods)
+        {
+            return new ConnectionInfo(
+                _host,
+                _port,
+                Users.Regular.UserName,
+                ProxyTypes.Socks4,
+                ProxyHost,
+                ProxyPort,
+                ProxyUserName,
+                ProxyPassword,
+                authenticationMethods);
+        }
+    }
+}
+

+ 147 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/AesCipherTests.cs

@@ -0,0 +1,147 @@
+using Renci.SshNet.IntegrationTests.Common;
+using Renci.SshNet.Security.Cryptography.Ciphers;
+using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
+using Renci.SshNet.TestTools.OpenSSH;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    [TestClass]
+    public class AesCipherTests : IntegrationTestBase
+    {
+        private IConnectionInfoFactory _adminConnectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort);
+            _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig();
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+        }
+
+        [TestMethod]
+        [Owner("olegkap")]
+        [TestCategory("Cipher")]
+        public void Test_Cipher_AEes128CBC_Connection()
+        {
+            _remoteSshdConfig.AddCipher(Cipher.Aes128Cbc)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.Encryptions.Clear();
+            connectionInfo.Encryptions.Add("aes128-cbc", new CipherInfo(128, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), null); }));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [Owner("olegkap")]
+        [TestCategory("Cipher")]
+        public void Test_Cipher_Aes192CBC_Connection()
+        {
+            _remoteSshdConfig.AddCipher(Cipher.Aes192Cbc)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.Encryptions.Clear();
+            connectionInfo.Encryptions.Add("aes192-cbc", new CipherInfo(192, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), null); }));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [Owner("olegkap")]
+        [TestCategory("Cipher")]
+        public void Test_Cipher_Aes256CBC_Connection()
+        {
+            _remoteSshdConfig.AddCipher(Cipher.Aes256Cbc)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.Encryptions.Clear();
+            connectionInfo.Encryptions.Add("aes256-cbc", new CipherInfo(256, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), null); }));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [Owner("olegkap")]
+        [TestCategory("Cipher")]
+        public void Test_Cipher_Aes128CTR_Connection()
+        {
+            _remoteSshdConfig.AddCipher(Cipher.Aes128Ctr)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.Encryptions.Clear();
+            connectionInfo.Encryptions.Add("aes128-ctr", new CipherInfo(128, (key, iv) => { return new AesCipher(key, new CtrCipherMode(iv), null); }));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [Owner("olegkap")]
+        [TestCategory("Cipher")]
+        public void Test_Cipher_Aes192CTR_Connection()
+        {
+            _remoteSshdConfig.AddCipher(Cipher.Aes192Ctr)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.Encryptions.Clear();
+            connectionInfo.Encryptions.Add("aes192-ctr", new CipherInfo(192, (key, iv) => { return new AesCipher(key, new CtrCipherMode(iv), null); }));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [Owner("olegkap")]
+        [TestCategory("Cipher")]
+        public void Test_Cipher_Aes256CTR_Connection()
+        {
+            _remoteSshdConfig.AddCipher(Cipher.Aes256Ctr)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.Encryptions.Clear();
+            connectionInfo.Encryptions.Add("aes256-ctr", new CipherInfo(256, (key, iv) => { return new AesCipher(key, new CtrCipherMode(iv), null); }));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+    }
+}

+ 158 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ForwardedPortLocalTest.cs

@@ -0,0 +1,158 @@
+using System.Diagnostics;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{    
+    /// <summary>
+    /// Provides functionality for local port forwarding
+    /// </summary>
+    [TestClass]
+    public class ForwardedPortLocalTest : IntegrationTestBase
+    {
+        [TestMethod]
+        [WorkItem(713)]
+        [Owner("Kenneth_aa")]
+        [TestCategory("PortForwarding")]
+        [Description("Test if calling Stop on ForwardedPortLocal instance causes wait.")]
+        public void Test_PortForwarding_Local_Stop_Hangs_On_Wait()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+
+                var port1 = new ForwardedPortLocal("localhost", 8084, "www.google.com", 80);
+                client.AddForwardedPort(port1);
+                port1.Exception += delegate (object sender, ExceptionEventArgs e)
+                {
+                    Assert.Fail(e.Exception.ToString());
+                };
+
+                port1.Start();
+
+                var hasTestedTunnel = false;
+
+                _ = ThreadPool.QueueUserWorkItem(delegate (object state)
+                {
+                    try
+                    {
+                        var url = "http://www.google.com/";
+                        Debug.WriteLine("Starting web request to \"" + url + "\"");
+
+#if NET6_0_OR_GREATER
+                        var httpClient = new HttpClient();
+                        var response = httpClient.GetAsync(url)
+                                                 .ConfigureAwait(false)
+                                                 .GetAwaiter()
+                                                 .GetResult();
+#else
+                            var request = (HttpWebRequest) WebRequest.Create(url);
+                            var response = (HttpWebResponse) request.GetResponse();
+#endif // NET6_0_OR_GREATER
+
+                        Assert.IsNotNull(response);
+
+                        Debug.WriteLine("Http Response status code: " + response.StatusCode.ToString());
+
+                        response.Dispose();
+
+                        hasTestedTunnel = true;
+                    }
+                    catch (Exception ex)
+                    {
+                        Assert.Fail(ex.ToString());
+                    }
+                });
+
+                // Wait for the web request to complete.
+                while (!hasTestedTunnel)
+                {
+                    Thread.Sleep(1000);
+                }
+
+                try
+                {
+                    // Try stop the port forwarding, wait 3 seconds and fail if it is still started.
+                    _ = ThreadPool.QueueUserWorkItem(delegate (object state)
+                    {
+                        Debug.WriteLine("Trying to stop port forward.");
+                        port1.Stop();
+                        Debug.WriteLine("Port forwarding stopped.");
+                    });
+
+                    Thread.Sleep(3000);
+                    if (port1.IsStarted)
+                    {
+                        Assert.Fail("Port forwarding not stopped.");
+                    }
+                }
+                catch (Exception ex)
+                {
+                    Assert.Fail(ex.ToString());
+                }
+                client.RemoveForwardedPort(port1);
+                client.Disconnect();
+                Debug.WriteLine("Success.");
+            }
+        }
+
+        [TestMethod]
+        [ExpectedException(typeof(SshConnectionException))]
+        public void Test_PortForwarding_Local_Without_Connecting()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                var port1 = new ForwardedPortLocal("localhost", 8084, "www.renci.org", 80);
+                client.AddForwardedPort(port1);
+                port1.Exception += delegate (object sender, ExceptionEventArgs e)
+                {
+                    Assert.Fail(e.Exception.ToString());
+                };
+                port1.Start();
+
+                var test = Parallel.For(0,
+                                 100,
+                                 counter =>
+                                 {
+                                     var start = DateTime.Now;
+
+#if NET6_0_OR_GREATER
+                                     var httpClient = new HttpClient();
+                                     using (var response = httpClient.GetAsync("http://localhost:8084").GetAwaiter().GetResult())
+                                     {
+                                         var data = ReadStream(response.Content.ReadAsStream());
+#else
+                                        var request = (HttpWebRequest) WebRequest.Create("http://localhost:8084");
+                                        using (var response = (HttpWebResponse) request.GetResponse())
+                                        {
+                                            var data = ReadStream(response.GetResponseStream());
+#endif // NET6_0_OR_GREATER
+                                         var end = DateTime.Now;
+
+                                         Debug.WriteLine(string.Format("Request# {2}: Lenght: {0} Time: {1}", data.Length, end - start, counter));
+                                     }
+                                 });
+            }
+        }
+
+        private static byte[] ReadStream(Stream stream)
+        {
+            var buffer = new byte[1024];
+            using (var ms = new MemoryStream())
+            {
+                while (true)
+                {
+                    var read = stream.Read(buffer, 0, buffer.Length);
+                    if (read > 0)
+                    {
+                        ms.Write(buffer, 0, read);
+                    }
+                    else
+                    {
+                        return ms.ToArray();
+                    }
+                }
+            }
+        }
+    }
+}

+ 67 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/HMacTest.cs

@@ -0,0 +1,67 @@
+using Renci.SshNet.Abstractions;
+using Renci.SshNet.IntegrationTests.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    [TestClass]
+    public class HMacTest : IntegrationTestBase
+    {
+        private IConnectionInfoFactory _adminConnectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort);
+            _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig();
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+        }
+
+        [TestMethod]
+        public void Test_HMac_Sha1_Connection()
+        {
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.HmacAlgorithms.Clear();
+            connectionInfo.HmacAlgorithms.Add("hmac-sha1", new HashInfo(20 * 8, CryptoAbstraction.CreateHMACSHA1));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_HMac_Sha256_Connection()
+        {
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.HmacAlgorithms.Clear();
+            connectionInfo.HmacAlgorithms.Add("hmac-sha2-256", new HashInfo(32 * 8, CryptoAbstraction.CreateHMACSHA256));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_HMac_Sha2_512_Connection()
+        {
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.HmacAlgorithms.Clear();
+            connectionInfo.HmacAlgorithms.Add("hmac-sha2-512", new HashInfo(64 * 8, CryptoAbstraction.CreateHMACSHA512));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+    }
+}

+ 336 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ScpClientTest.cs

@@ -0,0 +1,336 @@
+using System.Security.Cryptography;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Provides SCP client functionality.
+    /// </summary>
+    [TestClass]
+    public partial class ScpClientTest : IntegrationTestBase
+    {
+        [TestMethod]
+        [TestCategory("Scp")]
+        public void Test_Scp_File_Upload_Download()
+        {
+            RemoveAllFiles();
+
+            using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                scp.Connect();
+
+                var uploadedFileName = Path.GetTempFileName();
+                var downloadedFileName = Path.GetTempFileName();
+
+                CreateTestFile(uploadedFileName, 1);
+
+                scp.Upload(new FileInfo(uploadedFileName), Path.GetFileName(uploadedFileName));
+
+                scp.Download(Path.GetFileName(uploadedFileName), new FileInfo(downloadedFileName));
+
+                //  Calculate MD5 value
+                var uploadedHash = CalculateMD5(uploadedFileName);
+                var downloadedHash = CalculateMD5(downloadedFileName);
+
+                File.Delete(uploadedFileName);
+                File.Delete(downloadedFileName);
+
+                scp.Disconnect();
+
+                Assert.AreEqual(uploadedHash, downloadedHash);
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Scp")]
+        public void Test_Scp_Stream_Upload_Download()
+        {
+            RemoveAllFiles();
+
+            using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                scp.Connect();
+
+                var uploadedFileName = Path.GetTempFileName();
+                var downloadedFileName = Path.GetTempFileName();
+
+                CreateTestFile(uploadedFileName, 1);
+
+                //  Calculate has value
+                using (var stream = File.OpenRead(uploadedFileName))
+                {
+                    scp.Upload(stream, Path.GetFileName(uploadedFileName));
+                }
+
+                using (var stream = File.OpenWrite(downloadedFileName))
+                {
+                    scp.Download(Path.GetFileName(uploadedFileName), stream);
+                }
+
+                //  Calculate MD5 value
+                var uploadedHash = CalculateMD5(uploadedFileName);
+                var downloadedHash = CalculateMD5(downloadedFileName);
+
+                File.Delete(uploadedFileName);
+                File.Delete(downloadedFileName);
+
+                scp.Disconnect();
+
+                Assert.AreEqual(uploadedHash, downloadedHash);
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Scp")]
+        public void Test_Scp_10MB_File_Upload_Download()
+        {
+            RemoveAllFiles();
+
+            using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                scp.Connect();
+
+                var uploadedFileName = Path.GetTempFileName();
+                var downloadedFileName = Path.GetTempFileName();
+
+                CreateTestFile(uploadedFileName, 10);
+
+                scp.Upload(new FileInfo(uploadedFileName), Path.GetFileName(uploadedFileName));
+
+                scp.Download(Path.GetFileName(uploadedFileName), new FileInfo(downloadedFileName));
+
+                //  Calculate MD5 value
+                var uploadedHash = CalculateMD5(uploadedFileName);
+                var downloadedHash = CalculateMD5(downloadedFileName);
+
+                File.Delete(uploadedFileName);
+                File.Delete(downloadedFileName);
+
+                scp.Disconnect();
+
+                Assert.AreEqual(uploadedHash, downloadedHash);
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Scp")]
+        public void Test_Scp_10MB_Stream_Upload_Download()
+        {
+            RemoveAllFiles();
+
+            using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                scp.Connect();
+
+                var uploadedFileName = Path.GetTempFileName();
+                var downloadedFileName = Path.GetTempFileName();
+
+                CreateTestFile(uploadedFileName, 10);
+
+                //  Calculate has value
+                using (var stream = File.OpenRead(uploadedFileName))
+                {
+                    scp.Upload(stream, Path.GetFileName(uploadedFileName));
+                }
+
+                using (var stream = File.OpenWrite(downloadedFileName))
+                {
+                    scp.Download(Path.GetFileName(uploadedFileName), stream);
+                }
+
+                //  Calculate MD5 value
+                var uploadedHash = CalculateMD5(uploadedFileName);
+                var downloadedHash = CalculateMD5(downloadedFileName);
+
+                File.Delete(uploadedFileName);
+                File.Delete(downloadedFileName);
+
+                scp.Disconnect();
+
+                Assert.AreEqual(uploadedHash, downloadedHash);
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Scp")]
+        public void Test_Scp_Directory_Upload_Download()
+        {
+            RemoveAllFiles();
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+                sftp.CreateDirectory("uploaded_dir");
+            }
+
+            using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                scp.Connect();
+
+                var uploadDirectory =
+                    Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName()));
+                for (var i = 0; i < 3; i++)
+                {
+                    var subfolder = Directory.CreateDirectory(string.Format(@"{0}\folder_{1}", uploadDirectory.FullName, i));
+
+                    for (var j = 0; j < 5; j++)
+                    {
+                        CreateTestFile(string.Format(@"{0}\file_{1}", subfolder.FullName, j), 1);
+                    }
+
+                    CreateTestFile(string.Format(@"{0}\file_{1}", uploadDirectory.FullName, i), 1);
+                }
+
+                scp.Upload(uploadDirectory, "uploaded_dir");
+
+                var downloadDirectory =
+                    Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName()));
+
+                scp.Download("uploaded_dir", downloadDirectory);
+
+                var uploadedFiles = uploadDirectory.GetFiles("*.*", SearchOption.AllDirectories);
+                var downloadFiles = downloadDirectory.GetFiles("*.*", SearchOption.AllDirectories);
+
+                var result = from f1 in uploadedFiles
+                             from f2 in downloadFiles
+                             where
+                                 f1.FullName.Substring(uploadDirectory.FullName.Length) ==
+                                 f2.FullName.Substring(downloadDirectory.FullName.Length)
+                                 && CalculateMD5(f1.FullName) == CalculateMD5(f2.FullName)
+                             select f1;
+
+                var counter = result.Count();
+
+                scp.Disconnect();
+
+                Assert.IsTrue(counter == uploadedFiles.Length && uploadedFiles.Length == downloadFiles.Length);
+            }
+            RemoveAllFiles();
+        }
+
+        [TestMethod]
+        [TestCategory("Scp")]
+        public void Test_Scp_File_20_Parallel_Upload_Download()
+        {
+            using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                scp.Connect();
+
+                var uploadFilenames = new string[20];
+                for (var i = 0; i < uploadFilenames.Length; i++)
+                {
+                    uploadFilenames[i] = Path.GetTempFileName();
+                    CreateTestFile(uploadFilenames[i], 1);
+                }
+
+                _ = Parallel.ForEach(uploadFilenames,
+                                     filename =>
+                                        {
+                                            scp.Upload(new FileInfo(filename), Path.GetFileName(filename));
+                                        });
+                _ = Parallel.ForEach(uploadFilenames,
+                                     filename =>
+                                        {
+                                            scp.Download(Path.GetFileName(filename), new FileInfo(string.Format("{0}.down", filename)));
+                                        });
+
+                var result = from file in uploadFilenames
+                             where CalculateMD5(file) == CalculateMD5(string.Format("{0}.down", file))
+                             select file;
+
+                scp.Disconnect();
+
+                Assert.IsTrue(result.Count() == uploadFilenames.Length);
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Scp")]
+        public void Test_Scp_File_Upload_Download_Events()
+        {
+            using (var scp = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                scp.Connect();
+
+                var uploadFilenames = new string[10];
+
+                for (var i = 0; i < uploadFilenames.Length; i++)
+                {
+                    uploadFilenames[i] = Path.GetTempFileName();
+                    CreateTestFile(uploadFilenames[i], 1);
+                }
+
+                var uploadedFiles = uploadFilenames.ToDictionary(Path.GetFileName, (filename) => 0L);
+                var downloadedFiles = uploadFilenames.ToDictionary((filename) => string.Format("{0}.down", Path.GetFileName(filename)), (filename) => 0L);
+
+                scp.Uploading += delegate (object sender, ScpUploadEventArgs e)
+                {
+                    uploadedFiles[e.Filename] = e.Uploaded;
+                };
+
+                scp.Downloading += delegate (object sender, ScpDownloadEventArgs e)
+                {
+                    downloadedFiles[string.Format("{0}.down", e.Filename)] = e.Downloaded;
+                };
+
+                _ = Parallel.ForEach(uploadFilenames,
+                                     filename =>
+                                        {
+                                            scp.Upload(new FileInfo(filename), Path.GetFileName(filename));
+                                        });
+                _ = Parallel.ForEach(uploadFilenames,
+                                     filename =>
+                                        {
+                                            scp.Download(Path.GetFileName(filename), new FileInfo(string.Format("{0}.down", filename)));
+                                        });
+
+                var result = from uf in uploadedFiles
+                             from df in downloadedFiles
+                             where string.Format("{0}.down", uf.Key) == df.Key && uf.Value == df.Value
+                             select uf;
+
+                scp.Disconnect();
+
+                Assert.IsTrue(result.Count() == uploadFilenames.Length && uploadFilenames.Length == uploadedFiles.Count && uploadedFiles.Count == downloadedFiles.Count);
+            }
+        }
+
+        protected static string CalculateMD5(string fileName)
+        {
+            using (var file = new FileStream(fileName, FileMode.Open))
+            {
+#if NET7_0_OR_GREATER
+                var hash = MD5.HashData(file);
+#else
+#if NET6_0
+                var md5 = MD5.Create();
+#else
+                MD5 md5 = new MD5CryptoServiceProvider();
+#endif // NET6_0
+                var hash = md5.ComputeHash(file);
+#endif // NET7_0_OR_GREATER
+
+                file.Close();
+
+                var sb = new StringBuilder();
+
+                for (var i = 0; i < hash.Length; i++)
+                {
+                    _ = sb.Append(i.ToString("x2"));
+                }
+
+                return sb.ToString();
+            }
+        }
+
+        private void RemoveAllFiles()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                _ = client.RunCommand("rm -rf *");
+                client.Disconnect();
+            }
+        }
+    }
+}

+ 10 - 18
src/Renci.SshNet.Tests/Classes/SftpClientTest.ChangeDirectory.cs → src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ChangeDirectory.cs

@@ -1,21 +1,18 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
-using Renci.SshNet.Tests.Properties;
+using Renci.SshNet.Common;
 
-namespace Renci.SshNet.Tests.Classes
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
 {
     /// <summary>
     /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
     /// </summary>
-    public partial class SftpClientTest
+    public partial class SftpClientTest : IntegrationTestBase
     {
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(SftpPathNotFoundException))]
         public void Test_Sftp_ChangeDirectory_Root_Dont_Exists()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.ChangeDirectory("/asdasd");
@@ -24,11 +21,10 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(SftpPathNotFoundException))]
         public void Test_Sftp_ChangeDirectory_Root_With_Slash_Dont_Exists()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.ChangeDirectory("/asdasd/");
@@ -37,11 +33,10 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(SftpPathNotFoundException))]
         public void Test_Sftp_ChangeDirectory_Subfolder_Dont_Exists()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.ChangeDirectory("/asdasd/sssddds");
@@ -50,11 +45,10 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(SftpPathNotFoundException))]
         public void Test_Sftp_ChangeDirectory_Subfolder_With_Slash_Dont_Exists()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.ChangeDirectory("/asdasd/sssddds/");
@@ -63,10 +57,9 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         public void Test_Sftp_ChangeDirectory_Which_Exists()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.ChangeDirectory("/usr");
@@ -76,10 +69,9 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         public void Test_Sftp_ChangeDirectory_Which_Exists_With_Slash()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.ChangeDirectory("/usr/");
@@ -87,4 +79,4 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
     }
-}
+}

+ 72 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.CreateDirectory.cs

@@ -0,0 +1,72 @@
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+    /// </summary>
+    public partial class SftpClientTest : IntegrationTestBase
+    {
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_CreateDirectory_In_Current_Location()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.CreateDirectory("test-in-current");
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPermissionDeniedException))]
+        public void Test_Sftp_CreateDirectory_In_Forbidden_Directory()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, AdminUser.UserName, AdminUser.Password))
+            {
+                sftp.Connect();
+
+                sftp.CreateDirectory("/sbin/test");
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPathNotFoundException))]
+        public void Test_Sftp_CreateDirectory_Invalid_Path()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.CreateDirectory("/abcdefg/abcefg");
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SshException))]
+        public void Test_Sftp_CreateDirectory_Already_Exists()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.CreateDirectory("test");
+
+                sftp.CreateDirectory("test");
+
+                sftp.Disconnect();
+            }
+        }
+    }
+}

+ 71 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs

@@ -0,0 +1,71 @@
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+    /// </summary>
+    public partial class SftpClientTest : IntegrationTestBase
+    {
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPathNotFoundException))]
+        public void Test_Sftp_DeleteDirectory_Which_Doesnt_Exists()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.DeleteDirectory("abcdef");
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPermissionDeniedException))]
+        public void Test_Sftp_DeleteDirectory_Which_No_Permissions()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, AdminUser.UserName, AdminUser.Password))
+            {
+                sftp.Connect();
+
+                sftp.DeleteDirectory("/usr");
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_DeleteDirectory()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.CreateDirectory("abcdef");
+                sftp.DeleteDirectory("abcdef");
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [Description("Test passing null to DeleteDirectory.")]
+        [ExpectedException(typeof(ArgumentException))]
+        public void Test_Sftp_DeleteDirectory_Null()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.DeleteDirectory(null);
+
+                sftp.Disconnect();
+            }
+        }
+    }
+}

+ 10 - 25
src/Renci.SshNet.Tests/Classes/SftpClientTest.Download.cs → src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs

@@ -1,28 +1,18 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
-using Renci.SshNet.Tests.Properties;
-using System;
-using System.IO;
+using Renci.SshNet.Common;
 
-namespace Renci.SshNet.Tests.Classes
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
 {
     /// <summary>
     /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
     /// </summary>
-    public partial class SftpClientTest
+    public partial class SftpClientTest : IntegrationTestBase
     {
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(SftpPermissionDeniedException))]
         public void Test_Sftp_Download_Forbidden()
         {
-            if (Resources.USERNAME == "root")
-            {
-                Assert.Fail("Must not run this test as root!");
-            }
-
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, AdminUser.UserName, AdminUser.Password))
             {
                 sftp.Connect();
 
@@ -39,11 +29,10 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(SftpPathNotFoundException))]
         public void Test_Sftp_Download_File_Not_Exists()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
 
@@ -59,12 +48,11 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test passing null to BeginDownloadFile")]
         [ExpectedException(typeof(ArgumentNullException))]
         public void Test_Sftp_BeginDownloadFile_StreamIsNull()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.BeginDownloadFile("aaaa", null, null, null);
@@ -74,12 +62,11 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test passing null to BeginDownloadFile")]
         [ExpectedException(typeof(ArgumentException))]
         public void Test_Sftp_BeginDownloadFile_FileNameIsWhiteSpace()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.BeginDownloadFile("   ", new MemoryStream(), null, null);
@@ -89,12 +76,11 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test passing null to BeginDownloadFile")]
         [ExpectedException(typeof(ArgumentException))]
         public void Test_Sftp_BeginDownloadFile_FileNameIsNull()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 sftp.BeginDownloadFile(null, new MemoryStream(), null, null);
@@ -104,15 +90,14 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(ArgumentException))]
         public void Test_Sftp_EndDownloadFile_Invalid_Async_Handle()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 var filename = Path.GetTempFileName();
-                this.CreateTestFile(filename, 1);
+                CreateTestFile(filename, 1);
                 sftp.UploadFile(File.OpenRead(filename), "test123");
                 var async1 = sftp.BeginListDirectory("/", null, null);
                 var async2 = sftp.BeginDownloadFile("test123", new MemoryStream(), null, null);

+ 263 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs

@@ -0,0 +1,263 @@
+using System.Diagnostics;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+    /// </summary>
+    public partial class SftpClientTest : IntegrationTestBase
+    {
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPermissionDeniedException))]
+        public void Test_Sftp_ListDirectory_Permission_Denied()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                var files = sftp.ListDirectory("/root");
+                foreach (var file in files)
+                {
+                    Debug.WriteLine(file.FullName);
+                }
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPathNotFoundException))]
+        public void Test_Sftp_ListDirectory_Not_Exists()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                var files = sftp.ListDirectory("/asdfgh");
+                foreach (var file in files)
+                {
+                    Debug.WriteLine(file.FullName);
+                }
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_ListDirectory_Current()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                var files = sftp.ListDirectory(".");
+
+                Assert.IsTrue(files.Count() > 0);
+
+                foreach (var file in files)
+                {
+                    Debug.WriteLine(file.FullName);
+                }
+
+                sftp.Disconnect();
+            }
+        }
+
+#if NET6_0_OR_GREATER
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public async Task Test_Sftp_ListDirectoryAsync_Current()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+                var cts = new CancellationTokenSource();
+                cts.CancelAfter(TimeSpan.FromMinutes(1));
+                var count = 0;
+                await foreach(var file in sftp.ListDirectoryAsync(".", cts.Token))
+                {
+                    count++;
+                    Debug.WriteLine(file.FullName);
+                }
+
+                Assert.IsTrue(count > 0);
+
+                sftp.Disconnect();
+            }
+        }
+#endif
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_ListDirectory_Empty()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                var files = sftp.ListDirectory(string.Empty);
+
+                Assert.IsTrue(files.Count() > 0);
+
+                foreach (var file in files)
+                {
+                    Debug.WriteLine(file.FullName);
+                }
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [Description("Test passing null to ListDirectory.")]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void Test_Sftp_ListDirectory_Null()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                var files = sftp.ListDirectory(null);
+
+                Assert.IsTrue(files.Count() > 0);
+
+                foreach (var file in files)
+                {
+                    Debug.WriteLine(file.FullName);
+                }
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_ListDirectory_HugeDirectory()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                //  Create 10000 directory items
+                for (int i = 0; i < 10000; i++)
+                {
+                    sftp.CreateDirectory(string.Format("test_{0}", i));
+                }
+
+                var files = sftp.ListDirectory(".");
+
+                //  Ensure that directory has at least 10000 items
+                Assert.IsTrue(files.Count() > 10000);
+
+                sftp.Disconnect();
+            }
+
+            RemoveAllFiles();
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_Change_Directory()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet");
+
+                sftp.CreateDirectory("test1");
+
+                sftp.ChangeDirectory("test1");
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1");
+
+                sftp.CreateDirectory("test1_1");
+                sftp.CreateDirectory("test1_2");
+                sftp.CreateDirectory("test1_3");
+
+                var files = sftp.ListDirectory(".");
+
+                Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}", sftp.WorkingDirectory)));
+
+                sftp.ChangeDirectory("test1_1");
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
+
+                sftp.ChangeDirectory("../test1_2");
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_2");
+
+                sftp.ChangeDirectory("..");
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1");
+
+                sftp.ChangeDirectory("..");
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet");
+
+                files = sftp.ListDirectory("test1/test1_1");
+
+                Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}/test1/test1_1", sftp.WorkingDirectory)));
+
+                sftp.ChangeDirectory("test1/test1_1");
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
+
+                sftp.ChangeDirectory("/home/sshnet/test1/test1_1");
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
+
+                sftp.ChangeDirectory("/home/sshnet/test1/test1_1/../test1_2");
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_2");
+
+                sftp.ChangeDirectory("../../");
+
+                sftp.DeleteDirectory("test1/test1_1");
+                sftp.DeleteDirectory("test1/test1_2");
+                sftp.DeleteDirectory("test1/test1_3");
+                sftp.DeleteDirectory("test1");
+
+                sftp.Disconnect();
+            }
+
+            RemoveAllFiles();
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [Description("Test passing null to ChangeDirectory.")]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void Test_Sftp_ChangeDirectory_Null()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.ChangeDirectory(null);
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [Description("Test calling EndListDirectory method more then once.")]
+        [ExpectedException(typeof(ArgumentException))]
+        public void Test_Sftp_Call_EndListDirectory_Twice()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+                var ar = sftp.BeginListDirectory("/", null, null);
+                var result = sftp.EndListDirectory(ar);
+                var result1 = sftp.EndListDirectory(ar);
+            }
+        }
+    }
+}

+ 6 - 13
src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFile.cs → src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFile.cs

@@ -1,21 +1,15 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Tests.Properties;
-using System;
-using System.IO;
-
-namespace Renci.SshNet.Tests.Classes
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
 {
     /// <summary>
     /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
     /// </summary>
-    public partial class SftpClientTest
+    public partial class SftpClientTest : IntegrationTestBase
     {
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         public void Test_Sftp_Rename_File()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
 
@@ -23,7 +17,7 @@ namespace Renci.SshNet.Tests.Classes
                 string remoteFileName1 = Path.GetRandomFileName();
                 string remoteFileName2 = Path.GetRandomFileName();
 
-                this.CreateTestFile(uploadedFileName, 1);
+                CreateTestFile(uploadedFileName, 1);
 
                 using (var file = File.OpenRead(uploadedFileName))
                 {
@@ -42,12 +36,11 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test passing null to RenameFile.")]
         [ExpectedException(typeof(ArgumentNullException))]
         public void Test_Sftp_RenameFile_Null()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
 
@@ -57,4 +50,4 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
     }
-}
+}

+ 5 - 13
src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs → src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFileAsync.cs

@@ -1,22 +1,15 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Tests.Properties;
-using System;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace Renci.SshNet.Tests.Classes
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
 {
     /// <summary>
     /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
     /// </summary>
-    public partial class SftpClientTest
+    public partial class SftpClientTest : IntegrationTestBase
     {
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         public async Task Test_Sftp_RenameFileAsync()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 await sftp.ConnectAsync(default);
 
@@ -24,7 +17,7 @@ namespace Renci.SshNet.Tests.Classes
                 string remoteFileName1 = Path.GetRandomFileName();
                 string remoteFileName2 = Path.GetRandomFileName();
 
-                this.CreateTestFile(uploadedFileName, 1);
+                CreateTestFile(uploadedFileName, 1);
 
                 using (var file = File.OpenRead(uploadedFileName))
                 {
@@ -46,12 +39,11 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test passing null to RenameFile.")]
         [ExpectedException(typeof(ArgumentNullException))]
         public async Task Test_Sftp_RenameFileAsync_Null()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 await sftp.ConnectAsync(default);
 

+ 8 - 14
src/Renci.SshNet.Tests/Classes/SftpClientTest.SynchronizeDirectories.cs → src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.SynchronizeDirectories.cs

@@ -1,31 +1,26 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Tests.Properties;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
+using System.Diagnostics;
 
-namespace Renci.SshNet.Tests.Classes
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
 {
     /// <summary>
     /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
     /// </summary>
-    public partial class SftpClientTest
+    public partial class SftpClientTest : IntegrationTestBase
     {
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         public void Test_Sftp_SynchronizeDirectories()
         {
             RemoveAllFiles();
 
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
 
                 string uploadedFileName = Path.GetTempFileName();
 
                 string sourceDir = Path.GetDirectoryName(uploadedFileName);
-                string destDir = "/";
+                string destDir = "/home/sshnet/";
                 string searchPattern = Path.GetFileName(uploadedFileName);
                 var upLoadedFiles = sftp.SynchronizeDirectories(sourceDir, destDir, searchPattern);
 
@@ -42,19 +37,18 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         public void Test_Sftp_BeginSynchronizeDirectories()
         {
             RemoveAllFiles();
 
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
 
                 string uploadedFileName = Path.GetTempFileName();
 
                 string sourceDir = Path.GetDirectoryName(uploadedFileName);
-                string destDir = "/";
+                string destDir = "/home/sshnet/";
                 string searchPattern = Path.GetFileName(uploadedFileName);
 
                 var asyncResult = sftp.BeginSynchronizeDirectories(sourceDir,
@@ -83,4 +77,4 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
     }
-}
+}

+ 17 - 27
src/Renci.SshNet.Tests/Classes/SftpClientTest.Upload.cs → src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs

@@ -1,29 +1,20 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-using Renci.SshNet.Common;
+using Renci.SshNet.Common;
 using Renci.SshNet.Sftp;
-using Renci.SshNet.Tests.Properties;
 
-namespace Renci.SshNet.Tests.Classes
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
 {
     /// <summary>
     /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
     /// </summary>
-    public partial class SftpClientTest
+    public partial class SftpClientTest : IntegrationTestBase
     {
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         public void Test_Sftp_Upload_And_Download_1MB_File()
         {
             RemoveAllFiles();
 
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
 
@@ -62,11 +53,10 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(SftpPermissionDeniedException))]
         public void Test_Sftp_Upload_Forbidden()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
 
@@ -86,7 +76,6 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each()
         {
             var maxFiles = 10;
@@ -94,8 +83,9 @@ namespace Renci.SshNet.Tests.Classes
 
             RemoveAllFiles();
 
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
+                sftp.OperationTimeout = TimeSpan.FromMinutes(1);
                 sftp.Connect();
 
                 var testInfoList = new Dictionary<string, TestInfo>();
@@ -210,6 +200,11 @@ namespace Renci.SshNet.Tests.Classes
 
                     testInfo.DownloadedHash = CalculateMD5(testInfo.DownloadedFileName);
 
+                    Console.WriteLine(remoteFile);
+                    Console.WriteLine("UploadedBytes: "+ testInfo.UploadResult.UploadedBytes);
+                    Console.WriteLine("DownloadedBytes: " + testInfo.DownloadResult.DownloadedBytes);
+                    Console.WriteLine("UploadedHash: " + testInfo.UploadedHash);
+                    Console.WriteLine("DownloadedHash: " + testInfo.DownloadedHash);
                     if (!(testInfo.UploadResult.UploadedBytes > 0 && testInfo.DownloadResult.DownloadedBytes > 0 && testInfo.DownloadResult.DownloadedBytes == testInfo.UploadResult.UploadedBytes))
                     {
                         uploadDownloadSizeOk = false;
@@ -242,13 +237,12 @@ namespace Renci.SshNet.Tests.Classes
         //  TODO:   Split this test into multiple tests
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test that delegates passed to BeginUploadFile, BeginDownloadFile and BeginListDirectory are actually called.")]
         public void Test_Sftp_Ensure_Async_Delegates_Called_For_BeginFileUpload_BeginFileDownload_BeginListDirectory()
         {
             RemoveAllFiles();
 
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
 
@@ -330,12 +324,11 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test passing null to BeginUploadFile")]
         [ExpectedException(typeof(ArgumentNullException))]
         public void Test_Sftp_BeginUploadFile_StreamIsNull()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 _ = sftp.BeginUploadFile(null, "aaaaa", null, null);
@@ -345,12 +338,11 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test passing null to BeginUploadFile")]
         [ExpectedException(typeof(ArgumentException))]
         public void Test_Sftp_BeginUploadFile_FileNameIsWhiteSpace()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 _ = sftp.BeginUploadFile(new MemoryStream(), "   ", null, null);
@@ -360,12 +352,11 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [Description("Test passing null to BeginUploadFile")]
         [ExpectedException(typeof(ArgumentException))]
         public void Test_Sftp_BeginUploadFile_FileNameIsNull()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 _ = sftp.BeginUploadFile(new MemoryStream(), null, null, null);
@@ -375,11 +366,10 @@ namespace Renci.SshNet.Tests.Classes
 
         [TestMethod]
         [TestCategory("Sftp")]
-        [TestCategory("integration")]
         [ExpectedException(typeof(ArgumentException))]
         public void Test_Sftp_EndUploadFile_Invalid_Async_Handle()
         {
-            using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
             {
                 sftp.Connect();
                 var async1 = sftp.BeginListDirectory("/", null, null);

+ 68 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.cs

@@ -0,0 +1,68 @@
+using System.Security.Cryptography;
+
+using Renci.SshNet.Sftp;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+    /// </summary>
+    [TestClass]
+    public partial class SftpClientTest
+    {
+        protected static string CalculateMD5(string fileName)
+        {
+            using (FileStream file = new FileStream(fileName, FileMode.Open))
+            {
+                var hash = MD5.HashData(file);
+
+                file.Close();
+
+                StringBuilder sb = new StringBuilder();
+                for (var i = 0; i < hash.Length; i++)
+                {
+                    sb.Append(hash[i].ToString("x2"));
+                }
+                return sb.ToString();
+            }
+        }
+
+        private void RemoveAllFiles()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                client.RunCommand("rm -rf *");
+                client.Disconnect();
+            }
+        }
+
+        /// <summary>
+        /// Helper class to help with upload and download testing
+        /// </summary>
+        private class TestInfo
+        {
+            public string RemoteFileName { get; set; }
+
+            public string UploadedFileName { get; set; }
+
+            public string DownloadedFileName { get; set; }
+
+            //public ulong UploadedBytes { get; set; }
+
+            //public ulong DownloadedBytes { get; set; }
+
+            public FileStream UploadedFile { get; set; }
+
+            public FileStream DownloadedFile { get; set; }
+
+            public string UploadedHash { get; set; }
+
+            public string DownloadedHash { get; set; }
+
+            public SftpUploadAsyncResult UploadResult { get; set; }
+
+            public SftpDownloadAsyncResult DownloadResult { get; set; }
+        }
+    }
+}

+ 120 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpFileTest.cs

@@ -0,0 +1,120 @@
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Represents SFTP file information
+    /// </summary>
+    [TestClass]
+    public class SftpFileTest : IntegrationTestBase
+    {
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Get_Root_Directory()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+                var directory = sftp.Get("/");
+
+                Assert.AreEqual("/", directory.FullName);
+                Assert.IsTrue(directory.IsDirectory);
+                Assert.IsFalse(directory.IsRegularFile);
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPathNotFoundException))]
+        public void Test_Get_Invalid_Directory()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.Get("/xyz");
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Get_File()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.UploadFile(new MemoryStream(), "abc.txt");
+
+                var file = sftp.Get("abc.txt");
+
+                Assert.AreEqual("/home/sshnet/abc.txt", file.FullName);
+                Assert.IsTrue(file.IsRegularFile);
+                Assert.IsFalse(file.IsDirectory);
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [Description("Test passing null to Get.")]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void Test_Get_File_Null()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                var file = sftp.Get(null);
+
+                sftp.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Get_International_File()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                sftp.UploadFile(new MemoryStream(), "test-üöä-");
+
+                var file = sftp.Get("test-üöä-");
+
+                Assert.AreEqual("/home/sshnet/test-üöä-", file.FullName);
+                Assert.IsTrue(file.IsRegularFile);
+                Assert.IsFalse(file.IsDirectory);
+            }
+        }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public void Test_Sftp_SftpFile_MoveTo()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                sftp.Connect();
+
+                string uploadedFileName = Path.GetTempFileName();
+                string remoteFileName = Path.GetRandomFileName();
+                string newFileName = Path.GetRandomFileName();
+
+                CreateTestFile(uploadedFileName, 1);
+
+                using (var file = File.OpenRead(uploadedFileName))
+                {
+                    sftp.UploadFile(file, remoteFileName);
+                }
+
+                var sftpFile = sftp.Get(remoteFileName);
+
+                sftpFile.MoveTo(newFileName);
+
+                Assert.AreEqual(newFileName, sftpFile.Name);
+
+                sftp.Disconnect();
+            }
+        }
+    }
+}

+ 545 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs

@@ -0,0 +1,545 @@
+using System.Diagnostics;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Represents SSH command that can be executed.
+    /// </summary>
+    [TestClass]
+    public class SshCommandTest : IntegrationTestBase
+    {
+        [TestMethod]
+        public void Test_Run_SingleCommand()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                #region Example SshCommand RunCommand Result
+                client.Connect();
+
+                var testValue = Guid.NewGuid().ToString();
+                var command = client.RunCommand(string.Format("echo {0}", testValue));
+                var result = command.Result;
+                result = result.Substring(0, result.Length - 1);    //  Remove \n character returned by command
+
+                client.Disconnect();
+                #endregion
+
+                Assert.IsTrue(result.Equals(testValue));
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_SingleCommand()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                #region Example SshCommand CreateCommand Execute
+                client.Connect();
+
+                var testValue = Guid.NewGuid().ToString();
+                var command = string.Format("echo {0}", testValue);
+                var cmd = client.CreateCommand(command);
+                var result = cmd.Execute();
+                result = result.Substring(0, result.Length - 1);    //  Remove \n character returned by command
+
+                client.Disconnect();
+                #endregion
+
+                Assert.IsTrue(result.Equals(testValue));
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_OutputStream()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                #region Example SshCommand CreateCommand Execute OutputStream
+                client.Connect();
+
+                var cmd = client.CreateCommand("ls -l");   //  very long list
+                var asynch = cmd.BeginExecute();
+
+                var reader = new StreamReader(cmd.OutputStream);
+
+                while (!asynch.IsCompleted)
+                {
+                    var result = reader.ReadToEnd();
+                    if (string.IsNullOrEmpty(result))
+                    {
+                        continue;
+                    }
+
+                    Console.Write(result);
+                }
+
+                _ = cmd.EndExecute(asynch);
+
+                client.Disconnect();
+                #endregion
+
+                Assert.Inconclusive();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_ExtendedOutputStream()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                #region Example SshCommand CreateCommand Execute ExtendedOutputStream
+
+                client.Connect();
+                var cmd = client.CreateCommand("echo 12345; echo 654321 >&2");
+                var result = cmd.Execute();
+
+                Console.Write(result);
+
+                var reader = new StreamReader(cmd.ExtendedOutputStream);
+                Console.WriteLine("DEBUG:");
+                Console.Write(reader.ReadToEnd());
+
+                client.Disconnect();
+
+                #endregion
+
+                Assert.Inconclusive();
+            }
+        }
+
+        [TestMethod]
+        [ExpectedException(typeof(SshOperationTimeoutException))]
+        public void Test_Execute_Timeout()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                #region Example SshCommand CreateCommand Execute CommandTimeout
+                client.Connect();
+                var cmd = client.CreateCommand("sleep 10s");
+                cmd.CommandTimeout = TimeSpan.FromSeconds(5);
+                cmd.Execute();
+                client.Disconnect();
+                #endregion
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Infinite_Timeout()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                var cmd = client.CreateCommand("sleep 10s");
+                cmd.Execute();
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_InvalidCommand()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+
+                var cmd = client.CreateCommand(";");
+                cmd.Execute();
+                if (string.IsNullOrEmpty(cmd.Error))
+                {
+                    Assert.Fail("Operation should fail");
+                }
+                Assert.IsTrue(cmd.ExitStatus > 0);
+
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_InvalidCommand_Then_Execute_ValidCommand()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                var cmd = client.CreateCommand(";");
+                cmd.Execute();
+                if (string.IsNullOrEmpty(cmd.Error))
+                {
+                    Assert.Fail("Operation should fail");
+                }
+                Assert.IsTrue(cmd.ExitStatus > 0);
+
+                var result = ExecuteTestCommand(client);
+
+                client.Disconnect();
+
+                Assert.IsTrue(result);
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Command_with_ExtendedOutput()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                var cmd = client.CreateCommand("echo 12345; echo 654321 >&2");
+                cmd.Execute();
+
+                //var extendedData = Encoding.ASCII.GetString(cmd.ExtendedOutputStream.ToArray());
+                var extendedData = new StreamReader(cmd.ExtendedOutputStream, Encoding.ASCII).ReadToEnd();
+                client.Disconnect();
+
+                Assert.AreEqual("12345\n", cmd.Result);
+                Assert.AreEqual("654321\n", extendedData);
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Command_Reconnect_Execute_Command()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                var result = ExecuteTestCommand(client);
+                Assert.IsTrue(result);
+
+                client.Disconnect();
+                client.Connect();
+                result = ExecuteTestCommand(client);
+                Assert.IsTrue(result);
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Command_ExitStatus()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                #region Example SshCommand RunCommand ExitStatus
+                client.Connect();
+
+                var cmd = client.RunCommand("exit 128");
+                
+                Console.WriteLine(cmd.ExitStatus);
+
+                client.Disconnect();
+                #endregion
+
+                Assert.IsTrue(cmd.ExitStatus == 128);
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Command_Asynchronously()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+
+                var cmd = client.CreateCommand("sleep 5s; echo 'test'");
+                var asyncResult = cmd.BeginExecute(null, null);
+                while (!asyncResult.IsCompleted)
+                {
+                    Thread.Sleep(100);
+                }
+
+                cmd.EndExecute(asyncResult);
+
+                Assert.IsTrue(cmd.Result == "test\n");
+
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Command_Asynchronously_With_Error()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+
+                var cmd = client.CreateCommand("sleep 5s; ;");
+                var asyncResult = cmd.BeginExecute(null, null);
+                while (!asyncResult.IsCompleted)
+                {
+                    Thread.Sleep(100);
+                }
+
+                cmd.EndExecute(asyncResult);
+
+                Assert.IsFalse(string.IsNullOrEmpty(cmd.Error));
+
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Command_Asynchronously_With_Callback()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+
+                var callbackCalled = false;
+
+                var cmd = client.CreateCommand("sleep 5s; echo 'test'");
+                var asyncResult = cmd.BeginExecute(new AsyncCallback((s) =>
+                {
+                    callbackCalled = true;
+                }), null);
+                while (!asyncResult.IsCompleted)
+                {
+                    Thread.Sleep(100);
+                }
+
+                cmd.EndExecute(asyncResult);
+
+                Assert.IsTrue(callbackCalled);
+
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Command_Asynchronously_With_Callback_On_Different_Thread()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+
+                var currentThreadId = Thread.CurrentThread.ManagedThreadId;
+                int callbackThreadId = 0;
+
+                var cmd = client.CreateCommand("sleep 5s; echo 'test'");
+                var asyncResult = cmd.BeginExecute(new AsyncCallback((s) =>
+                {
+                    callbackThreadId = Thread.CurrentThread.ManagedThreadId;
+                }), null);
+                while (!asyncResult.IsCompleted)
+                {
+                    Thread.Sleep(100);
+                }
+
+                cmd.EndExecute(asyncResult);
+
+                Assert.AreNotEqual(currentThreadId, callbackThreadId);
+
+                client.Disconnect();
+            }
+        }
+
+        /// <summary>
+        /// Tests for Issue 563.
+        /// </summary>
+        [WorkItem(563), TestMethod]
+        public void Test_Execute_Command_Same_Object_Different_Commands()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                var cmd = client.CreateCommand("echo 12345");
+                cmd.Execute();
+                Assert.AreEqual("12345\n", cmd.Result);
+                cmd.Execute("echo 23456");
+                Assert.AreEqual("23456\n", cmd.Result);
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Get_Result_Without_Execution()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                var cmd = client.CreateCommand("ls -l");
+
+                Assert.IsTrue(string.IsNullOrEmpty(cmd.Result));
+                client.Disconnect();
+            }
+        }
+
+        [TestMethod]
+        public void Test_Get_Error_Without_Execution()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                var cmd = client.CreateCommand("ls -l");
+
+                Assert.IsTrue(string.IsNullOrEmpty(cmd.Error));
+                client.Disconnect();
+            }
+        }
+
+        [WorkItem(703), TestMethod]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void Test_EndExecute_Before_BeginExecute()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                var cmd = client.CreateCommand("ls -l");
+                cmd.EndExecute(null);
+                client.Disconnect();
+            }
+        }
+
+        /// <summary>
+        ///A test for BeginExecute
+        ///</summary>
+        [TestMethod()]
+        public void BeginExecuteTest()
+        {
+            string expected = "123\n";
+            string result;
+
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                #region Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute
+
+                client.Connect();
+
+                var cmd = client.CreateCommand("sleep 15s;echo 123"); // Perform long running task
+
+                var asynch = cmd.BeginExecute();
+
+                while (!asynch.IsCompleted)
+                {
+                    //  Waiting for command to complete...
+                    Thread.Sleep(2000);
+                }
+                result = cmd.EndExecute(asynch);
+                client.Disconnect();
+
+                #endregion
+
+                Assert.IsNotNull(asynch);
+                Assert.AreEqual(expected, result);
+            }
+        }
+
+        [TestMethod]
+        public void Test_Execute_Invalid_Command()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                #region Example SshCommand CreateCommand Error
+
+                client.Connect();
+
+                var cmd = client.CreateCommand(";");
+                cmd.Execute();
+                if (!string.IsNullOrEmpty(cmd.Error))
+                {
+                    Console.WriteLine(cmd.Error);
+                }
+
+                client.Disconnect();
+
+                #endregion
+
+                Assert.Inconclusive();
+            }
+        }
+
+        [TestMethod]
+        public void Test_MultipleThread_Example_MultipleConnections()
+        {
+            try
+            {
+#region Example SshCommand RunCommand Parallel
+                Parallel.For(0, 100,
+                    () =>
+                    {
+                        var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password);
+                        client.Connect();
+                        return client;
+                    },
+                    (int counter, ParallelLoopState pls, SshClient client) =>
+                    {
+                        var result = client.RunCommand("echo 123");
+                        Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter));
+                        return client;
+                    },
+                    (SshClient client) =>
+                    {
+                        client.Disconnect();
+                        client.Dispose();
+                    }
+                );
+#endregion
+
+            }
+            catch (Exception exp)
+            {
+                Assert.Fail(exp.ToString());
+            }
+        }
+
+        [TestMethod]
+        
+        public void Test_MultipleThread_100_MultipleConnections()
+        {
+            try
+            {
+                Parallel.For(0, 100,
+                    () =>
+                    {
+                        var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password);
+                        client.Connect();
+                        return client;
+                    },
+                    (int counter, ParallelLoopState pls, SshClient client) =>
+                    {
+                        var result = ExecuteTestCommand(client);
+                        Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter));
+                        Assert.IsTrue(result);
+                        return client;
+                    },
+                    (SshClient client) =>
+                    {
+                        client.Disconnect();
+                        client.Dispose();
+                    }
+                );
+            }
+            catch (Exception exp)
+            {
+                Assert.Fail(exp.ToString());
+            }
+        }
+
+        [TestMethod]
+        public void Test_MultipleThread_100_MultipleSessions()
+        {
+            using (var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                client.Connect();
+                Parallel.For(0, 100,
+                    (counter) =>
+                    {
+                        var result = ExecuteTestCommand(client);
+                        Debug.WriteLine(string.Format("TestMultipleThreadMultipleConnections #{0}", counter));
+                        Assert.IsTrue(result);
+                    }
+                );
+
+                client.Disconnect();
+            }
+        }
+
+        private static bool ExecuteTestCommand(SshClient s)
+        {
+            var testValue = Guid.NewGuid().ToString();
+            var command = string.Format("echo {0}", testValue);
+            var cmd = s.CreateCommand(command);
+            var result = cmd.Execute();
+            result = result.Substring(0, result.Length - 1);    //  Remove \n character returned by command
+            return result.Equals(testValue);
+        }
+    }
+}

+ 50 - 0
src/Renci.SshNet.IntegrationTests/OldIntegrationTests/TripleDesCipherTest.cs

@@ -0,0 +1,50 @@
+using Renci.SshNet.IntegrationTests.Common;
+using Renci.SshNet.Security.Cryptography.Ciphers;
+using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
+using Renci.SshNet.TestTools.OpenSSH;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+    /// <summary>
+    /// Implements 3DES cipher algorithm.
+    /// </summary>
+    [TestClass]
+    public class TripleDesCipherTest : IntegrationTestBase
+    {
+        private IConnectionInfoFactory _adminConnectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort);
+            _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig();
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+        }
+        
+        [TestMethod]
+        [Owner("olegkap")]
+        [TestCategory("Cipher")]
+        public void Test_Cipher_TripleDESCBC_Connection()
+        {
+            _remoteSshdConfig.AddCipher(Cipher.TripledesCbc)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = new PasswordConnectionInfo(SshServerHostName, SshServerPort, User.UserName, User.Password);
+            connectionInfo.Encryptions.Clear();
+            connectionInfo.Encryptions.Add("3des-cbc", new CipherInfo(192, (key, iv) => { return new TripleDesCipher(key, new CbcCipherMode(iv), null); }));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+                client.Disconnect();
+            }
+        }
+    }
+}

+ 84 - 0
src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs

@@ -0,0 +1,84 @@
+using Renci.SshNet.IntegrationTests.Common;
+using Renci.SshNet.TestTools.OpenSSH;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    [TestClass]
+    public class PrivateKeyAuthenticationTests : TestBase
+        {
+        private IConnectionInfoFactory _connectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort);
+            _remoteSshdConfig = new RemoteSshd(new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort)).OpenConfig();
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+        }
+
+        [TestMethod]
+        public void Ecdsa256()
+        {
+            _remoteSshdConfig.AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.EcdsaSha2Nistp256)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod("key_ecdsa_256_openssh"));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void Ecdsa384()
+        {
+            _remoteSshdConfig.AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.EcdsaSha2Nistp384)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod("key_ecdsa_384_openssh"));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        [TestMethod]
+        public void EcdsaA521()
+        {
+            _remoteSshdConfig.AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.EcdsaSha2Nistp521)
+                             .Update()
+                             .Restart();
+
+            var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod("key_ecdsa_521_openssh"));
+
+            using (var client = new SshClient(connectionInfo))
+            {
+                client.Connect();
+            }
+        }
+
+        private PrivateKeyAuthenticationMethod CreatePrivateKeyAuthenticationMethod(string keyResource)
+        {
+            var privateKey = CreatePrivateKeyFromManifestResource("Renci.SshNet.IntegrationTests.resources.client." + keyResource);
+            return new PrivateKeyAuthenticationMethod(Users.Regular.UserName, privateKey);
+        }
+
+        private PrivateKeyFile CreatePrivateKeyFromManifestResource(string resourceName)
+        {
+            using (var stream = GetManifestResourceStream(resourceName))
+            {
+                return new PrivateKeyFile(stream);
+            }
+        }
+    }
+}

+ 11 - 0
src/Renci.SshNet.IntegrationTests/Program.cs

@@ -0,0 +1,11 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    class Program
+    {
+#if NETFRAMEWORK
+        private static void Main()
+        {
+        }
+#endif
+    }
+}

+ 246 - 0
src/Renci.SshNet.IntegrationTests/RemoteSshd.cs

@@ -0,0 +1,246 @@
+using Renci.SshNet.TestTools.OpenSSH;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    internal class RemoteSshd
+    {
+        private readonly IConnectionInfoFactory _connectionInfoFactory;
+
+        public RemoteSshd(IConnectionInfoFactory connectionInfoFactory)
+        {
+            _connectionInfoFactory = connectionInfoFactory;
+        }
+
+        public RemoteSshdConfig OpenConfig()
+        {
+            return new RemoteSshdConfig(this, _connectionInfoFactory);
+        }
+
+        public RemoteSshd Restart()
+        {
+            // Restart SSH daemon
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                // Kill all processes that start with 'sshd' and that run as root
+                var stopCommand = client.CreateCommand("sudo pkill -9 -U 0 sshd.pam");
+                var stopOutput = stopCommand.Execute();
+                if (stopCommand.ExitStatus != 0)
+                {
+                    throw new ApplicationException($"Stopping ssh service failed with exit code {stopCommand.ExitStatus}.\r\n{stopOutput}\r\n{stopCommand.Error}");
+                }
+
+                var resetFailedCommand = client.CreateCommand("sudo /usr/sbin/sshd.pam");
+                var resetFailedOutput = resetFailedCommand.Execute();
+                if (resetFailedCommand.ExitStatus != 0)
+                {
+                    throw new ApplicationException($"Reset failures for ssh service failed with exit code {resetFailedCommand.ExitStatus}.\r\n{resetFailedOutput}\r\n{resetFailedCommand.Error}");
+                }
+            }
+
+            return this;
+        }
+    }
+
+    internal class RemoteSshdConfig
+    {
+        private const string SshdConfigFilePath = "/etc/ssh/sshd_config";
+        private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true);
+
+        private readonly RemoteSshd _remoteSshd;
+        private readonly IConnectionInfoFactory _connectionInfoFactory;
+        private readonly SshdConfig _config;
+
+        public RemoteSshdConfig(RemoteSshd remoteSshd, IConnectionInfoFactory connectionInfoFactory)
+        {
+            _remoteSshd = remoteSshd;
+            _connectionInfoFactory = connectionInfoFactory;
+
+            using (var client = new ScpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var memoryStream = new MemoryStream())
+                {
+                    client.Download(SshdConfigFilePath, memoryStream);
+
+                    memoryStream.Position = 0;
+                    _config = SshdConfig.LoadFrom(memoryStream, Encoding.UTF8);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Specifies whether challenge-response authentication is allowed.
+        /// </summary>
+        /// <param name="value"><see langword="true"/> to allow challenge-response authentication.</param>
+        /// <returns>
+        /// The current <see cref="RemoteSshdConfig"/> instance.
+        /// </returns>
+        public RemoteSshdConfig WithChallengeResponseAuthentication(bool? value)
+        {
+            _config.ChallengeResponseAuthentication = value;
+            return this;
+        }
+
+        /// <summary>
+        /// Specifies whether to allow keyboard-interactive authentication.
+        /// </summary>
+        /// <param name="value"><see langword="true"/> to allow keyboard-interactive authentication.</param>
+        /// <returns>
+        /// The current <see cref="RemoteSshdConfig"/> instance.
+        /// </returns>
+        public RemoteSshdConfig WithKeyboardInteractiveAuthentication(bool value)
+        {
+            _config.KeyboardInteractiveAuthentication = value;
+            return this;
+        }
+
+        /// <summary>
+        /// Specifies whether <c>sshd</c> should print /etc/motd when a user logs in interactively.
+        /// </summary>
+        /// <param name="value"><see langword="true"/> if <c>sshd</c> should print /etc/motd when a user logs in interactively.</param>
+        /// <returns>
+        /// The current <see cref="RemoteSshdConfig"/> instance.
+        /// </returns>
+        public RemoteSshdConfig PrintMotd(bool? value = true)
+        {
+            _config.PrintMotd = value;
+            return this;
+        }
+
+        /// <summary>
+        /// Specifies whether TCP forwarding is permitted.
+        /// </summary>
+        /// <param name="value"><see langword="true"/> to allow TCP forwarding.</param>
+        /// <returns>
+        /// The current <see cref="RemoteSshdConfig"/> instance.
+        /// </returns>
+        public RemoteSshdConfig AllowTcpForwarding(bool? value = true)
+        {
+            _config.AllowTcpForwarding = value;
+            return this;
+        }
+
+        public RemoteSshdConfig WithAuthenticationMethods(string user, string authenticationMethods)
+        {
+            var sshNetMatch = _config.Matches.FirstOrDefault(m => m.Users.Contains(user));
+            if (sshNetMatch == null)
+            {
+                sshNetMatch = new Match(new[] { user }, new string[0]);
+                _config.Matches.Add(sshNetMatch);
+            }
+
+            sshNetMatch.AuthenticationMethods = authenticationMethods;
+
+            return this;
+        }
+
+        public RemoteSshdConfig ClearCiphers()
+        {
+            _config.Ciphers.Clear();
+            return this;
+        }
+
+        public RemoteSshdConfig AddCipher(Cipher cipher)
+        {
+            _config.Ciphers.Add(cipher);
+            return this;
+        }
+
+        public RemoteSshdConfig ClearKeyExchangeAlgorithms()
+        {
+            _config.KeyExchangeAlgorithms.Clear();
+            return this;
+        }
+
+        public RemoteSshdConfig AddKeyExchangeAlgorithm(KeyExchangeAlgorithm keyExchangeAlgorithm)
+        {
+            _config.KeyExchangeAlgorithms.Add(keyExchangeAlgorithm);
+            return this;
+        }
+
+        public RemoteSshdConfig ClearPublicKeyAcceptedAlgorithms()
+        {
+            _config.PublicKeyAcceptedAlgorithms.Clear();
+            return this;
+        }
+
+        public RemoteSshdConfig AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm publicKeyAlgorithm)
+        {
+            _config.PublicKeyAcceptedAlgorithms.Add(publicKeyAlgorithm);
+            return this;
+        }
+
+        public RemoteSshdConfig ClearHostKeyAlgorithms()
+        {
+            _config.HostKeyAlgorithms.Clear();
+            return this;
+        }
+
+        public RemoteSshdConfig AddHostKeyAlgorithm(HostKeyAlgorithm hostKeyAlgorithm)
+        {
+            _config.HostKeyAlgorithms.Add(hostKeyAlgorithm);
+            return this;
+        }
+
+        public RemoteSshdConfig ClearSubsystems()
+        {
+            _config.Subsystems.Clear();
+            return this;
+        }
+
+        public RemoteSshdConfig AddSubsystem(Subsystem subsystem)
+        {
+            _config.Subsystems.Add(subsystem);
+            return this;
+        }
+
+        public RemoteSshdConfig WithLogLevel(LogLevel logLevel)
+        {
+            _config.LogLevel = logLevel;
+            return this;
+        }
+
+        public RemoteSshdConfig WithUsePAM(bool usePAM)
+        {
+            _config.UsePAM = usePAM;
+            return this;
+        }
+
+        public RemoteSshdConfig ClearHostKeyFiles()
+        {
+            _config.HostKeyFiles.Clear();
+            return this;
+        }
+
+        public RemoteSshdConfig AddHostKeyFile(string hostKeyFile)
+        {
+            _config.HostKeyFiles.Add(hostKeyFile);
+            return this;
+        }
+
+        public RemoteSshd Update()
+        {
+            using (var client = new ScpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var memoryStream = new MemoryStream())
+                using (var sw = new StreamWriter(memoryStream, Utf8NoBom))
+                {
+                    sw.NewLine = "\n";
+                    _config.SaveTo(sw);
+                    sw.Flush();
+
+                    memoryStream.Position = 0;
+
+                    client.Upload(memoryStream, SshdConfigFilePath);
+                }
+            }
+
+            return _remoteSshd;
+        }
+    }
+}

+ 20 - 5
src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj → src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj

@@ -3,7 +3,6 @@
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
-    <Nullable>enable</Nullable>
 
     <IsPackable>false</IsPackable>
     <IsTestProject>true</IsTestProject>
@@ -16,16 +15,21 @@
           We can stop producing XML docs for test projects (and remove the NoWarn for CS1591) once the following issue is fixed:
           https://github.com/dotnet/roslyn/issues/41640.
       -->
-    <NoWarn>$(NoWarn);CS1591</NoWarn>
+    <NoWarn>$(NoWarn);CS1591;SYSLIB0021;SYSLIB1045;SYSLIB0014;IDE0220;IDE0010</NoWarn>
       
   </PropertyGroup>
 
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'net7.0' ">
+    <DefineConstants>TRACE;FEATURE_MSTEST_DATATEST;FEATURE_SOCKET_EAP;FEATURE_ENCODING_ASCII;FEATURE_THREAD_SLEEP;FEATURE_THREAD_THREADPOOL</DefineConstants>
+  </PropertyGroup>
+
   <ItemGroup>
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
     <PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
     <PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
-      
+    
     <PackageReference Include="Testcontainers" Version="3.4.0" />
+    <PackageReference Include="System.Management" Version="4.7.0" />
     <!--
         Testcontainers has a dependency on SSH.NET which causes build warnings during assembly resolution:      
         
@@ -35,7 +39,7 @@
              
         To fix, we explicitly exclude the SSH.NET nuget package from this project's dependencies.
     -->
-    <PackageReference Include="SSH.NET" Version="2020.0.2" ExcludeAssets="All"/>
+    <PackageReference Include="SSH.NET" Version="2020.0.2" ExcludeAssets="All" />
       
     <PackageReference Include="coverlet.collector" Version="6.0.0">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -44,7 +48,18 @@
   </ItemGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\Renci.SshNet.TestTools.OpenSSH\Renci.SshNet.TestTools.OpenSSH.csproj" />
     <ProjectReference Include="..\Renci.SshNet\Renci.SshNet.csproj" />
   </ItemGroup>
-
+  <ItemGroup>
+    <EmbeddedResource Include="resources\client\id_dsa" />
+    <EmbeddedResource Include="resources\client\id_dsa.ppk" />
+    <EmbeddedResource Include="resources\client\id_noaccess.rsa" />
+    <EmbeddedResource Include="resources\client\id_rsa" />
+    <EmbeddedResource Include="resources\client\id_rsa_with_pass" />
+    <EmbeddedResource Include="resources\client\key_ecdsa_256_openssh" />
+    <EmbeddedResource Include="resources\client\key_ecdsa_384_openssh" />
+    <EmbeddedResource Include="resources\client\key_ecdsa_521_openssh" />
+    <EmbeddedResource Include="resources\issue #70.png" />
+  </ItemGroup>
 </Project>

+ 1 - 3
src/Renci.SshNet.IntegrationTests/ScpClientTests.cs

@@ -1,6 +1,4 @@
-using Renci.SshNet;
-
-namespace IntegrationTests
+namespace Renci.SshNet.IntegrationTests
 {
     /// <summary>
     /// The SCP client integration tests

+ 2379 - 0
src/Renci.SshNet.IntegrationTests/ScpTests.cs

@@ -0,0 +1,2379 @@
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    // TODO SCP: UPLOAD / DOWNLOAD ZERO LENGTH FILES
+    // TODO SCP: UPLOAD / DOWNLOAD EMPTY DIRECTORY
+    // TODO SCP: UPLOAD DIRECTORY THAT ALREADY EXISTS ON REMOTE HOST
+
+    [TestClass]
+    public class ScpTests : TestBase
+    {
+        private IConnectionInfoFactory _connectionInfoFactory;
+        private IRemotePathTransformation _remotePathTransformation;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort);
+            _remotePathTransformation = RemotePathTransformation.ShellQuote;
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadStreamDirectoryDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_Stream_DirectoryDoesNotExist()
+        {
+            foreach (var data in GetScpDownloadStreamDirectoryDoesNotExistData())
+            {
+                Scp_Download_Stream_DirectoryDoesNotExist((IRemotePathTransformation) data[0],
+                                                           (string) data[1],
+                                                           (string) data[2]);
+            }
+        }
+#endif
+        public void Scp_Download_Stream_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                              string remotePath,
+                                                              string remoteFile)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            // remove complete directory if it's not the home directory of the user
+            // or else remove the remote file
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(completeRemotePath))
+                {
+                    client.DeleteFile(completeRemotePath);
+                }
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+            }
+
+            try
+            {
+                using (var downloaded = new MemoryStream())
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Download(completeRemotePath, downloaded);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadStreamFileDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_Stream_FileDoesNotExist()
+        {
+            foreach (var data in GetScpDownloadStreamFileDoesNotExistData())
+            {
+                Scp_Download_Stream_FileDoesNotExist((IRemotePathTransformation)data[0],
+                                                      (string)data[1],
+                                                      (string)data[2]);
+            }
+        }
+#endif
+        public void Scp_Download_Stream_FileDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                         string remotePath,
+                                                         string remoteFile)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            // remove complete directory if it's not the home directory of the user
+            // or else remove the remote file
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(completeRemotePath))
+                {
+                    client.DeleteFile(completeRemotePath);
+                }
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+
+                    client.CreateDirectory(remotePath);
+                }
+            }
+
+            try
+            {
+                using (var downloaded = new MemoryStream())
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Download(completeRemotePath, downloaded);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadDirectoryInfoDirectoryDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_DirectoryInfo_DirectoryDoesNotExist()
+        {
+            foreach (var data in GetScpDownloadDirectoryInfoDirectoryDoesNotExistData())
+            {
+                Scp_Download_DirectoryInfo_DirectoryDoesNotExist((IRemotePathTransformation)data[0],
+                                                                 (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Download_DirectoryInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                                     string remotePath)
+        {
+            var localDirectory = Path.GetTempFileName();
+            File.Delete(localDirectory);
+            Directory.CreateDirectory(localDirectory);
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Download(remotePath, new DirectoryInfo(localDirectory));
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                Directory.Delete(localDirectory, true);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadDirectoryInfoExistingFileData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_DirectoryInfo_ExistingFile()
+        {
+            foreach (var data in GetScpDownloadDirectoryInfoExistingFileData())
+            {
+                Scp_Download_DirectoryInfo_ExistingFile((IRemotePathTransformation)data[0],
+                                                         (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Download_DirectoryInfo_ExistingFile(IRemotePathTransformation remotePathTransformation,
+                                                            string remotePath)
+        {
+            var content = CreateMemoryStream(100);
+            content.Position = 0;
+
+            var localDirectory = Path.GetTempFileName();
+            File.Delete(localDirectory);
+            Directory.CreateDirectory(localDirectory);
+
+            var localFile = Path.Combine(localDirectory, PosixPath.GetFileName(remotePath));
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+                    client.UploadFile(content, remotePath);
+                }
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Download(remotePath, new DirectoryInfo(localDirectory));
+                }
+
+                Assert.IsTrue(File.Exists(localFile));
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remotePath, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateFileHash(localFile), CreateHash(downloaded));
+                    }
+                }
+            }
+            finally
+            {
+                Directory.Delete(localDirectory, true);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteFile(remotePath);
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadDirectoryInfoExistingDirectoryData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_DirectoryInfo_ExistingDirectory()
+        {
+            foreach (var data in GetScpDownloadDirectoryInfoExistingDirectoryData())
+            {
+                Scp_Download_DirectoryInfo_ExistingDirectory((IRemotePathTransformation)data[0],
+                                                             (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Download_DirectoryInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
+                                                                 string remotePath)
+        {
+            var localDirectory = Path.GetTempFileName();
+            File.Delete(localDirectory);
+            Directory.CreateDirectory(localDirectory);
+
+            var localPathFile1 = Path.Combine(localDirectory, "file1 23");
+            var remotePathFile1 = CombinePaths(remotePath, "file1 23");
+            var contentFile1 = CreateMemoryStream(1024);
+            contentFile1.Position = 0;
+
+            var localPathFile2 = Path.Combine(localDirectory, "file2 #$%");
+            var remotePathFile2 = CombinePaths(remotePath, "file2 #$%");
+            var contentFile2 = CreateMemoryStream(2048);
+            contentFile2.Position = 0;
+
+            var localPathSubDirectory = Path.Combine(localDirectory, "subdir $1%#");
+            var remotePathSubDirectory = CombinePaths(remotePath, "subdir $1%#");
+
+            var localPathFile3 = Path.Combine(localPathSubDirectory, "file3 %$#");
+            var remotePathFile3 = CombinePaths(remotePathSubDirectory, "file3 %$#");
+            var contentFile3 = CreateMemoryStream(256);
+            contentFile3.Position = 0;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remotePathFile1))
+                {
+                    client.DeleteFile(remotePathFile1);
+                }
+
+                if (client.Exists(remotePathFile2))
+                {
+                    client.DeleteFile(remotePathFile2);
+                }
+
+                if (client.Exists(remotePathFile3))
+                {
+                    client.DeleteFile(remotePathFile3);
+                }
+
+                if (client.Exists(remotePathSubDirectory))
+                {
+                    client.DeleteDirectory(remotePathSubDirectory);
+                }
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+            }
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (!client.Exists(remotePath))
+                    {
+                        client.CreateDirectory(remotePath);
+                    }
+
+                    client.UploadFile(contentFile1, remotePathFile1);
+                    client.UploadFile(contentFile1, remotePathFile2);
+
+                    client.CreateDirectory(remotePathSubDirectory);
+                    client.UploadFile(contentFile3, remotePathFile3);
+                }
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Download(remotePath, new DirectoryInfo(localDirectory));
+                }
+
+                var localFiles = Directory.GetFiles(localDirectory);
+                Assert.AreEqual(2, localFiles.Length);
+                Assert.IsTrue(localFiles.Contains(localPathFile1));
+                Assert.IsTrue(localFiles.Contains(localPathFile2));
+
+                var localSubDirecties = Directory.GetDirectories(localDirectory);
+                Assert.AreEqual(1, localSubDirecties.Length);
+                Assert.AreEqual(localPathSubDirectory, localSubDirecties[0]);
+
+                var localFilesSubDirectory = Directory.GetFiles(localPathSubDirectory);
+                Assert.AreEqual(1, localFilesSubDirectory.Length);
+                Assert.AreEqual(localPathFile3, localFilesSubDirectory[0]);
+
+                Assert.AreEqual(0, Directory.GetDirectories(localPathSubDirectory).Length);
+            }
+            finally
+            {
+                Directory.Delete(localDirectory, true);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remotePathFile1))
+                    {
+                        client.DeleteFile(remotePathFile1);
+                    }
+
+                    if (client.Exists(remotePathFile2))
+                    {
+                        client.DeleteFile(remotePathFile2);
+                    }
+
+                    if (client.Exists(remotePathFile3))
+                    {
+                        client.DeleteFile(remotePathFile3);
+                    }
+
+                    if (client.Exists(remotePathSubDirectory))
+                    {
+                        client.DeleteDirectory(remotePathSubDirectory);
+                    }
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadFileInfoDirectoryDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_FileInfo_DirectoryDoesNotExist()
+        {
+            foreach (var data in GetScpDownloadFileInfoDirectoryDoesNotExistData())
+            {
+                Scp_Download_FileInfo_DirectoryDoesNotExist((IRemotePathTransformation)data[0],
+                                                            (string)data[1],
+                                                            (string)data[2]);
+            }
+        }
+#endif
+        public void Scp_Download_FileInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                                string remotePath,
+                                                                string remoteFile)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            // remove complete directory if it's not the home directory of the user
+            // or else remove the remote file
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+            }
+
+            var fileInfo = new FileInfo(Path.GetTempFileName());
+
+            try
+            {
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Download(completeRemotePath, fileInfo);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                fileInfo.Delete();
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadFileInfoFileDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_FileInfo_FileDoesNotExist()
+        {
+            foreach (var data in GetScpDownloadFileInfoFileDoesNotExistData())
+            {
+                Scp_Download_FileInfo_FileDoesNotExist((IRemotePathTransformation)data[0],
+                                                        (string)data[1],
+                                                        (string)data[2]);
+            }
+        }
+#endif
+        public void Scp_Download_FileInfo_FileDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                           string remotePath,
+                                                           string remoteFile)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            // remove complete directory if it's not the home directory of the user
+            // or else remove the remote file
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+
+                    client.CreateDirectory(remotePath);
+                }
+            }
+
+            var fileInfo = new FileInfo(Path.GetTempFileName());
+
+            try
+            {
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Download(completeRemotePath, fileInfo);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {completeRemotePath}: No such file or directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                fileInfo.Delete();
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadFileInfoExistingDirectoryData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_FileInfo_ExistingDirectory()
+        {
+            foreach (var data in GetScpDownloadFileInfoExistingDirectoryData())
+            {
+                Scp_Download_FileInfo_ExistingDirectory((IRemotePathTransformation)data[0],
+                                                        (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Download_FileInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
+                                                            string remotePath)
+        {
+            // remove complete directory if it's not the home directory of the user
+            // or else remove the remote file
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+
+                    client.CreateDirectory(remotePath);
+                }
+            }
+
+            var fileInfo = new FileInfo(Path.GetTempFileName());
+            fileInfo.Delete();
+
+            try
+            {
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Download(remotePath, fileInfo);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remotePath}: not a regular file", ex.Message);
+                    }
+
+                    Assert.IsFalse(fileInfo.Exists);
+                }
+            }
+            finally
+            {
+                fileInfo.Delete();
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadFileInfoExistingFileData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_FileInfo_ExistingFile()
+        {
+            foreach (var data in GetScpDownloadFileInfoExistingFileData())
+            {
+                Scp_Download_FileInfo_ExistingFile((IRemotePathTransformation)data[0],
+                                                        (string)data[1],
+                                                        (string)data[2],
+                                                        (int)data[3]);
+            }
+        }
+#endif
+        public void Scp_Download_FileInfo_ExistingFile(IRemotePathTransformation remotePathTransformation,
+                                                       string remotePath,
+                                                       string remoteFile,
+                                                       int size)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            // remove complete directory if it's not the home directory of the user
+            // or else remove the remote file
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(completeRemotePath))
+                {
+                    client.DeleteFile(completeRemotePath);
+                }
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+
+                    client.CreateDirectory(remotePath);
+                }
+            }
+
+            var fileInfo = new FileInfo(Path.GetTempFileName());
+
+            try
+            {
+                var content = CreateMemoryStream(size);
+                content.Position = 0;
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Upload(content, completeRemotePath);
+                }
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Download(completeRemotePath, fileInfo);
+                }
+
+                using (var fs = fileInfo.OpenRead())
+                {
+                    var downloadedBytes = new byte[fs.Length];
+                    Assert.AreEqual(downloadedBytes.Length, fs.Read(downloadedBytes, 0, downloadedBytes.Length));
+                    content.Position = 0;
+                    Assert.AreEqual(CreateHash(content), CreateHash(downloadedBytes));
+                }
+            }
+            finally
+            {
+                fileInfo.Delete();
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadStreamExistingDirectoryData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_Stream_ExistingDirectory()
+        {
+            foreach (var data in GetScpDownloadStreamExistingDirectoryData())
+            {
+                Scp_Download_Stream_ExistingDirectory((IRemotePathTransformation)data[0],
+                                                      (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Download_Stream_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
+                                                          string remotePath)
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+
+                    client.CreateDirectory(remotePath);
+                }
+            }
+
+            var file = Path.GetTempFileName();
+            File.Delete(file);
+
+            try
+            {
+                using (var fs = File.OpenWrite(file))
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Download(remotePath, fs);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remotePath}: not a regular file", ex.Message);
+                    }
+
+                    Assert.AreEqual(0, fs.Length);
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpDownloadStreamExistingFileData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Download_Stream_ExistingFile()
+        {
+            foreach (var data in GetScpDownloadStreamExistingFileData())
+            {
+                Scp_Download_Stream_ExistingFile((IRemotePathTransformation)data[0],
+                                                 (string)data[1],
+                                                 (string)data[2],
+                                                 (int)data[3]);
+            }
+        }
+#endif
+        public void Scp_Download_Stream_ExistingFile(IRemotePathTransformation remotePathTransformation,
+                                                     string remotePath,
+                                                     string remoteFile,
+                                                     int size)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            // remove complete directory if it's not the home directory of the user
+            // or else remove the remote file
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(completeRemotePath))
+                {
+                    client.DeleteFile(completeRemotePath);
+                }
+
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+
+                    client.CreateDirectory(remotePath);
+                }
+            }
+
+            var file = CreateTempFile(size);
+
+            try
+            {
+                using (var fs = File.OpenRead(file))
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Upload(fs, completeRemotePath);
+                }
+
+                using (var fs = File.OpenRead(file))
+                using (var downloaded = new MemoryStream(size))
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    client.Download(completeRemotePath, downloaded);
+                    downloaded.Position = 0;
+                    Assert.AreEqual(CreateHash(fs), CreateHash(downloaded));
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadFileStreamDirectoryDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_FileStream_DirectoryDoesNotExist()
+        {
+            foreach (var data in GetScpUploadFileStreamDirectoryDoesNotExistData())
+            {
+                Scp_Upload_FileStream_DirectoryDoesNotExist((IRemotePathTransformation)data[0],
+                                                            (string)data[1],
+                                                            (string)data[2]);
+            }
+        }
+#endif
+        public void Scp_Upload_FileStream_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                                string remotePath,
+                                                                string remoteFile)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(completeRemotePath))
+                {
+                    client.DeleteFile(completeRemotePath);
+                }
+
+                if (client.Exists(remotePath))
+                {
+                    client.DeleteDirectory(remotePath);
+                }
+            }
+
+            var file = CreateTempFile(1000);
+
+            try
+            {
+                using (var fs = File.OpenRead(file))
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Upload(fs, completeRemotePath);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadFileStreamExistingDirectoryData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_FileStream_ExistingDirectory()
+        {
+            foreach (var data in GetScpUploadFileStreamExistingDirectoryData())
+            {
+                Scp_Upload_FileStream_ExistingDirectory((IRemotePathTransformation)data[0],
+                                                        (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Upload_FileStream_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
+                                                            string remoteFile)
+        {
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile)))
+                {
+                    command.Execute();
+                }
+            }
+
+            var file = CreateTempFile(1000);
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+                    client.CreateDirectory(remoteFile);
+                }
+
+                using (var fs = File.OpenRead(file))
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Upload(fs, remoteFile);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remoteFile}: Is a directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteDirectory(remoteFile);
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(ScpUploadFileStreamExistingFileData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_FileStream_ExistingFile()
+        {
+            foreach (var data in ScpUploadFileStreamExistingFileData())
+            {
+                Scp_Upload_FileStream_ExistingFile((IRemotePathTransformation)data[0],
+                                                   (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Upload_FileStream_ExistingFile(IRemotePathTransformation remotePathTransformation,
+                                                       string remoteFile)
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+            }
+
+            // original content is bigger than new content to ensure file is fully overwritten
+            var originalContent = CreateMemoryStream(2000);
+            var file = CreateTempFile(1000);
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    originalContent.Position = 0;
+                    client.UploadFile(originalContent, remoteFile);
+                }
+
+                using (var fs = File.OpenRead(file))
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Upload(fs, remoteFile);
+                }
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    using (var downloaded = new MemoryStream(1000))
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded));
+                    }
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadFileStreamFileDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_FileStream_FileDoesNotExist()
+        {
+            foreach (var data in GetScpUploadFileStreamFileDoesNotExistData())
+            {
+                Scp_Upload_FileStream_FileDoesNotExist((IRemotePathTransformation)data[0],
+                                                       (string)data[1],
+                                                       (string)data[2],
+                                                       (int)data[3]);
+            }
+        }
+#endif
+        public void Scp_Upload_FileStream_FileDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                           string remotePath,
+                                                           string remoteFile,
+                                                           int size)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(completeRemotePath))
+                {
+                    client.DeleteFile(completeRemotePath);
+                }
+
+                // remove complete directory if it's not the home directory of the user
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+            }
+
+            var file = CreateTempFile(size);
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    // create directory if it's not the home directory of the user
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (!client.Exists((remotePath)))
+                        {
+                            client.CreateDirectory(remotePath);
+                        }
+                    }
+                }
+
+                using (var fs = File.OpenRead(file))
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Upload(fs, completeRemotePath);
+                }
+
+                using (var fs = File.OpenRead(file))
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    var sftpFile = client.Get(completeRemotePath);
+                    Assert.AreEqual(GetAbsoluteRemotePath(client, remotePath, remoteFile), sftpFile.FullName);
+                    Assert.AreEqual(size, sftpFile.Length);
+
+                    var downloaded = client.ReadAllBytes(completeRemotePath);
+                    Assert.AreEqual(CreateHash(fs), CreateHash(downloaded));
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    // remove complete directory if it's not the home directory of the user
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// https://github.com/sshnet/SSH.NET/issues/289
+        /// </summary>
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadFileInfoDirectoryDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_FileInfo_DirectoryDoesNotExist()
+        {
+            foreach (var data in GetScpUploadFileInfoDirectoryDoesNotExistData())
+            {
+                Scp_Upload_FileInfo_DirectoryDoesNotExist((IRemotePathTransformation)data[0],
+                                                          (string)data[1],
+                                                          (string)data[2]);
+            }
+        }
+#endif
+        public void Scp_Upload_FileInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                              string remotePath,
+                                                              string remoteFile)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(completeRemotePath))
+                {
+                    client.DeleteFile(completeRemotePath);
+                }
+
+                if (client.Exists(remotePath))
+                {
+                    client.DeleteDirectory(remotePath);
+                }
+            }
+
+            var file = CreateTempFile(1000);
+
+            try
+            {
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Upload(new FileInfo(file), completeRemotePath);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remotePath}: No such file or directory", ex.Message);
+                    }
+                }
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    Assert.IsFalse(client.Exists(completeRemotePath));
+                    Assert.IsFalse(client.Exists(remotePath));
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.DeleteFile(completeRemotePath);
+                    }
+
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// https://github.com/sshnet/SSH.NET/issues/286
+        /// </summary>
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadFileInfoExistingDirectoryData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_FileInfo_ExistingDirectory()
+        {
+            foreach (var data in GetScpUploadFileInfoExistingDirectoryData())
+            {
+                Scp_Upload_FileInfo_ExistingDirectory((IRemotePathTransformation)data[0],
+                                                      (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Upload_FileInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
+                                                          string remoteFile)
+        {
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile)))
+                {
+                    command.Execute();
+                }
+            }
+
+            var file = CreateTempFile(1000);
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+                    client.CreateDirectory(remoteFile);
+                }
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Upload(new FileInfo(file), remoteFile);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remoteFile}: Is a directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SshClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteFile)))
+                    {
+                        command.Execute();
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadFileInfoExistingFileData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_FileInfo_ExistingFile()
+        {
+            foreach (var data in GetScpUploadFileInfoExistingFileData())
+            {
+                Scp_Upload_FileInfo_ExistingFile((IRemotePathTransformation)data[0],
+                                                 (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Upload_FileInfo_ExistingFile(IRemotePathTransformation remotePathTransformation,
+                                                     string remoteFile)
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+            }
+
+            // original content is bigger than new content to ensure file is fully overwritten
+            var originalContent = CreateMemoryStream(2000);
+            var file = CreateTempFile(1000);
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    originalContent.Position = 0;
+                    client.UploadFile(originalContent, remoteFile);
+                }
+
+                var fileInfo = new FileInfo(file)
+                    {
+                        LastAccessTimeUtc = new DateTime(1973, 8, 13, 20, 15, 33, DateTimeKind.Utc),
+                        LastWriteTimeUtc = new DateTime(1974, 1, 24, 3, 55, 12, DateTimeKind.Utc)
+                    };
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Upload(fileInfo, remoteFile);
+                }
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    var uploadedFile = client.Get(remoteFile);
+                    Assert.AreEqual(fileInfo.LastAccessTimeUtc, uploadedFile.LastAccessTimeUtc);
+                    Assert.AreEqual(fileInfo.LastWriteTimeUtc, uploadedFile.LastWriteTimeUtc);
+
+                    using (var downloaded = new MemoryStream(1000))
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded));
+                    }
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadFileInfoFileDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_FileInfo_FileDoesNotExist()
+        {
+            foreach (var data in GetScpUploadFileInfoFileDoesNotExistData())
+            {
+                Scp_Upload_FileInfo_FileDoesNotExist((IRemotePathTransformation)data[0],
+                                                     (string)data[1],
+                                                     (string)data[2],
+                                                     (int)data[3]);
+            }
+        }
+#endif
+        public void Scp_Upload_FileInfo_FileDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                         string remotePath,
+                                                         string remoteFile,
+                                                         int size)
+        {
+            var completeRemotePath = CombinePaths(remotePath, remoteFile);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(completeRemotePath))
+                {
+                    client.DeleteFile(completeRemotePath);
+                }
+
+                // remove complete directory if it's not the home directory of the user
+                if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                {
+                    if (client.Exists(remotePath))
+                    {
+                        client.DeleteDirectory(remotePath);
+                    }
+                }
+            }
+
+            var file = CreateTempFile(size);
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    // create directory if it's not the home directory of the user
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (!client.Exists(remotePath))
+                        {
+                            client.CreateDirectory(remotePath);
+                        }
+                    }
+                }
+
+                var fileInfo = new FileInfo(file)
+                    {
+                        LastAccessTimeUtc = new DateTime(1973, 8, 13, 20, 15, 33, DateTimeKind.Utc),
+                        LastWriteTimeUtc = new DateTime(1974, 1, 24, 3, 55, 12, DateTimeKind.Utc)
+                    };
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Upload(fileInfo, completeRemotePath);
+                }
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    var uploadedFile = client.Get(completeRemotePath);
+                    Assert.AreEqual(fileInfo.LastAccessTimeUtc, uploadedFile.LastAccessTimeUtc);
+                    Assert.AreEqual(fileInfo.LastWriteTimeUtc, uploadedFile.LastWriteTimeUtc);
+                    Assert.AreEqual(size, uploadedFile.Length);
+
+                    using (var downloaded = new MemoryStream(size))
+                    {
+                        client.DownloadFile(completeRemotePath, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateFileHash(file), CreateHash(downloaded));
+                    }
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(completeRemotePath))
+                    {
+                        client.Delete(completeRemotePath);
+                    }
+
+                    // remove complete directory if it's not the home directory of the user
+                    if (remotePath.Length > 0 && remotePath != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remotePath))
+                        {
+                            client.DeleteDirectory(remotePath);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadDirectoryInfoDirectoryDoesNotExistData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_DirectoryInfo_DirectoryDoesNotExist()
+        {
+            foreach (var data in GetScpUploadDirectoryInfoDirectoryDoesNotExistData())
+            {
+                Scp_Upload_DirectoryInfo_DirectoryDoesNotExist((IRemotePathTransformation)data[0],
+                                                               (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Upload_DirectoryInfo_DirectoryDoesNotExist(IRemotePathTransformation remotePathTransformation,
+                                                                   string remoteDirectory)
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists((remoteDirectory)))
+                {
+                    client.DeleteDirectory(remoteDirectory);
+                }
+            }
+
+            var localDirectory = Path.GetTempFileName();
+            File.Delete(localDirectory);
+            Directory.CreateDirectory(localDirectory);
+
+            try
+            {
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    try
+                    {
+                        client.Upload(new DirectoryInfo(localDirectory), remoteDirectory);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remoteDirectory}: No such file or directory", ex.Message);
+                    }
+                }
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    Assert.IsFalse(client.Exists(remoteDirectory));
+                }
+            }
+            finally
+            {
+                Directory.Delete(localDirectory, true);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists((remoteDirectory)))
+                    {
+                        client.DeleteDirectory(remoteDirectory);
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadDirectoryInfoExistingDirectoryData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_DirectoryInfo_ExistingDirectory()
+        {
+            foreach (var data in GetScpUploadDirectoryInfoExistingDirectoryData())
+            {
+                Scp_Upload_DirectoryInfo_ExistingDirectory((IRemotePathTransformation)data[0],
+                                                           (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Upload_DirectoryInfo_ExistingDirectory(IRemotePathTransformation remotePathTransformation,
+                                                               string remoteDirectory)
+        {
+            string absoluteRemoteDirectory = GetAbsoluteRemotePath(_connectionInfoFactory, remoteDirectory);
+
+            var remotePathFile1 = CombinePaths(remoteDirectory, "file1");
+            var remotePathFile2 = CombinePaths(remoteDirectory, "file2");
+
+            var absoluteremoteSubDirectory1 = CombinePaths(absoluteRemoteDirectory, "sub1");
+            var remoteSubDirectory1 = CombinePaths(remoteDirectory, "sub1");
+            var remotePathSubFile1 = CombinePaths(remoteSubDirectory1, "file1");
+            var remotePathSubFile2 = CombinePaths(remoteSubDirectory1, "file2");
+            var absoluteremoteSubDirectory2 = CombinePaths(absoluteRemoteDirectory, "sub2");
+            var remoteSubDirectory2 = CombinePaths(remoteDirectory, "sub2");
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remotePathSubFile1))
+                {
+                    client.DeleteFile(remotePathSubFile1);
+                }
+                if (client.Exists(remotePathSubFile2))
+                {
+                    client.DeleteFile(remotePathSubFile2);
+                }
+                if (client.Exists(remoteSubDirectory1))
+                {
+                    client.DeleteDirectory(remoteSubDirectory1);
+                }
+                if (client.Exists(remoteSubDirectory2))
+                {
+                    client.DeleteDirectory(remoteSubDirectory2);
+                }
+                if (client.Exists(remotePathFile1))
+                {
+                    client.DeleteFile(remotePathFile1);
+                }
+                if (client.Exists(remotePathFile2))
+                {
+                    client.DeleteFile(remotePathFile2);
+                }
+
+                if (remoteDirectory.Length > 0 && remoteDirectory != "." && remoteDirectory != client.WorkingDirectory)
+                {
+                    if (client.Exists(remoteDirectory))
+                    {
+                        client.DeleteDirectory(remoteDirectory);
+                    }
+
+                    client.CreateDirectory(remoteDirectory);
+                }
+            }
+
+            var localDirectory = Path.GetTempFileName();
+            File.Delete(localDirectory);
+            Directory.CreateDirectory(localDirectory);
+
+            var localPathFile1 = Path.Combine(localDirectory, "file1");
+            var localPathFile2 = Path.Combine(localDirectory, "file2");
+
+            var localSubDirectory1 = Path.Combine(localDirectory, "sub1");
+            var localPathSubFile1 = Path.Combine(localSubDirectory1, "file1");
+            var localPathSubFile2 = Path.Combine(localSubDirectory1, "file2");
+            var localSubDirectory2 = Path.Combine(localDirectory, "sub2");
+
+            try
+            {
+                CreateFile(localPathFile1, 2000);
+                File.SetLastWriteTimeUtc(localPathFile1, new DateTime(2015, 8, 24, 5, 32, 16, DateTimeKind.Utc));
+
+                CreateFile(localPathFile2, 1000);
+                File.SetLastWriteTimeUtc(localPathFile2, new DateTime(2012, 3, 27, 23, 2, 54, DateTimeKind.Utc));
+
+                // create subdirectory with two files
+                Directory.CreateDirectory(localSubDirectory1);
+                CreateFile(localPathSubFile1, 1000);
+                File.SetLastWriteTimeUtc(localPathSubFile1, new DateTime(2013, 4, 12, 16, 54, 22, DateTimeKind.Utc));
+                CreateFile(localPathSubFile2, 2000);
+                File.SetLastWriteTimeUtc(localPathSubFile2, new DateTime(2015, 8, 4, 12, 43, 12, DateTimeKind.Utc));
+                Directory.SetLastWriteTimeUtc(localSubDirectory1,
+                                              new DateTime(2014, 6, 12, 13, 2, 44, DateTimeKind.Utc));
+
+                // create empty subdirectory
+                Directory.CreateDirectory(localSubDirectory2);
+                Directory.SetLastWriteTimeUtc(localSubDirectory2,
+                                              new DateTime(2011, 5, 14, 1, 5, 12, DateTimeKind.Utc));
+
+                Directory.SetLastWriteTimeUtc(localDirectory, new DateTime(2015, 10, 14, 22, 45, 11, DateTimeKind.Utc));
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+                    client.Upload(new DirectoryInfo(localDirectory), remoteDirectory);
+                }
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    Assert.IsTrue(client.Exists(remoteDirectory));
+
+                    var remoteSftpDirectory = client.Get(remoteDirectory);
+                    Assert.IsNotNull(remoteSftpDirectory);
+                    Assert.AreEqual(absoluteRemoteDirectory, remoteSftpDirectory.FullName);
+                    Assert.IsTrue(remoteSftpDirectory.IsDirectory);
+                    Assert.IsFalse(remoteSftpDirectory.IsRegularFile);
+
+                    // Due to CVE-2018-20685, we can no longer set the times or modes on a file or directory
+                    // that refers to the current directory ('.'), the parent directory ('..') or a directory
+                    // containing a forward slash ('/').
+                    Assert.AreNotEqual(Directory.GetLastWriteTimeUtc(localDirectory), remoteSftpDirectory.LastWriteTimeUtc);
+
+                    Assert.IsTrue(client.Exists(remotePathFile1));
+                    Assert.AreEqual(CreateFileHash(localPathFile1), CreateRemoteFileHash(client, remotePathFile1));
+                    var remoteSftpFile = client.Get(remotePathFile1);
+                    Assert.IsNotNull(remoteSftpFile);
+                    Assert.IsFalse(remoteSftpFile.IsDirectory);
+                    Assert.IsTrue(remoteSftpFile.IsRegularFile);
+                    Assert.AreEqual(File.GetLastWriteTimeUtc(localPathFile1), remoteSftpFile.LastWriteTimeUtc);
+
+                    Assert.IsTrue(client.Exists(remotePathFile2));
+                    Assert.AreEqual(CreateFileHash(localPathFile2), CreateRemoteFileHash(client, remotePathFile2));
+                    remoteSftpFile = client.Get(remotePathFile2);
+                    Assert.IsNotNull(remoteSftpFile);
+                    Assert.IsFalse(remoteSftpFile.IsDirectory);
+                    Assert.IsTrue(remoteSftpFile.IsRegularFile);
+                    Assert.AreEqual(File.GetLastWriteTimeUtc(localPathFile2), remoteSftpFile.LastWriteTimeUtc);
+
+                    Assert.IsTrue(client.Exists(remoteSubDirectory1));
+                    remoteSftpDirectory = client.Get(remoteSubDirectory1);
+                    Assert.IsNotNull(remoteSftpDirectory);
+                    Assert.AreEqual(absoluteremoteSubDirectory1, remoteSftpDirectory.FullName);
+                    Assert.IsTrue(remoteSftpDirectory.IsDirectory);
+                    Assert.IsFalse(remoteSftpDirectory.IsRegularFile);
+                    Assert.AreEqual(Directory.GetLastWriteTimeUtc(localSubDirectory1), remoteSftpDirectory.LastWriteTimeUtc);
+
+                    Assert.IsTrue(client.Exists(remotePathSubFile1));
+                    Assert.AreEqual(CreateFileHash(localPathSubFile1), CreateRemoteFileHash(client, remotePathSubFile1));
+
+                    Assert.IsTrue(client.Exists(remotePathSubFile2));
+                    Assert.AreEqual(CreateFileHash(localPathSubFile2), CreateRemoteFileHash(client, remotePathSubFile2));
+
+                    Assert.IsTrue(client.Exists(remoteSubDirectory2));
+                    remoteSftpDirectory = client.Get(remoteSubDirectory2);
+                    Assert.IsNotNull(remoteSftpDirectory);
+                    Assert.AreEqual(absoluteremoteSubDirectory2, remoteSftpDirectory.FullName);
+                    Assert.IsTrue(remoteSftpDirectory.IsDirectory);
+                    Assert.IsFalse(remoteSftpDirectory.IsRegularFile);
+                    Assert.AreEqual(Directory.GetLastWriteTimeUtc(localSubDirectory2), remoteSftpDirectory.LastWriteTimeUtc);
+                }
+            }
+            finally
+            {
+                Directory.Delete(localDirectory, true);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remotePathSubFile1))
+                    {
+                        client.DeleteFile(remotePathSubFile1);
+                    }
+                    if (client.Exists(remotePathSubFile2))
+                    {
+                        client.DeleteFile(remotePathSubFile2);
+                    }
+                    if (client.Exists(remoteSubDirectory1))
+                    {
+                        client.DeleteDirectory(remoteSubDirectory1);
+                    }
+                    if (client.Exists(remoteSubDirectory2))
+                    {
+                        client.DeleteDirectory(remoteSubDirectory2);
+                    }
+                    if (client.Exists(remotePathFile1))
+                    {
+                        client.DeleteFile(remotePathFile1);
+                    }
+                    if (client.Exists(remotePathFile2))
+                    {
+                        client.DeleteFile(remotePathFile2);
+                    }
+
+                    if (remoteDirectory.Length > 0 && remoteDirectory != "." && remoteDirectory != client.WorkingDirectory)
+                    {
+                        if (client.Exists(remoteDirectory))
+                        {
+                            client.DeleteDirectory(remoteDirectory);
+                        }
+                    }
+                }
+            }
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetScpUploadDirectoryInfoExistingFileData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Scp_Upload_DirectoryInfo_ExistingFile()
+        {
+            foreach (var data in GetScpUploadDirectoryInfoExistingFileData())
+            {
+                Scp_Upload_DirectoryInfo_ExistingFile((IRemotePathTransformation)data[0],
+                                                      (string)data[1]);
+            }
+        }
+#endif
+        public void Scp_Upload_DirectoryInfo_ExistingFile(IRemotePathTransformation remotePathTransformation,
+                                                          string remoteDirectory)
+        {
+            var remotePathFile1 = CombinePaths(remoteDirectory, "file1");
+            var remotePathFile2 = CombinePaths(remoteDirectory, "file2");
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                Console.WriteLine(client.ConnectionInfo.CurrentKeyExchangeAlgorithm);
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    command.Execute();
+                }
+            }
+
+            var localDirectory = Path.GetTempFileName();
+            File.Delete(localDirectory);
+            Directory.CreateDirectory(localDirectory);
+
+            var localPathFile1 = Path.Combine(localDirectory, "file1");
+            var localPathFile2 = Path.Combine(localDirectory, "file2");
+
+            try
+            {
+                CreateFile(localPathFile1, 50);
+                CreateFile(localPathFile2, 50);
+
+                using (var client = new ScpClient(_connectionInfoFactory.Create()))
+                {
+                    if (remotePathTransformation != null)
+                    {
+                        client.RemotePathTransformation = remotePathTransformation;
+                    }
+
+                    client.Connect();
+
+                    CreateRemoteFile(client, remoteDirectory, 10);
+
+                    try
+                    {
+                        client.Upload(new DirectoryInfo(localDirectory), remoteDirectory);
+                        Assert.Fail();
+                    }
+                    catch (ScpException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual($"scp: {remoteDirectory}: Not a directory", ex.Message);
+                    }
+                }
+            }
+            finally
+            {
+                Directory.Delete(localDirectory, true);
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    if (client.Exists(remotePathFile1))
+                    {
+                        client.DeleteFile(remotePathFile1);
+                    }
+                    if (client.Exists(remotePathFile2))
+                    {
+                        client.DeleteFile(remotePathFile2);
+                    }
+                    if (client.Exists((remoteDirectory)))
+                    {
+                        client.DeleteFile(remoteDirectory);
+                    }
+                }
+            }
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadStreamDirectoryDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" };
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadFileInfoFileDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "test123", 0 };
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "test123", 5 * 1024 * 1024 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 };
+            yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 };
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
+            yield return new object[] { null, "", "scp-issue280", 1024 };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadFileStreamFileDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 0 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 };
+            yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
+            yield return new object[] { RemotePathTransformation.None, "", "scp-issue280", 1024 };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadDirectoryInfoExistingDirectoryData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "scp-directorydoesnotexist" };
+            yield return new object[] { RemotePathTransformation.None, "." };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%" };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadDirectoryInfoExistingFileData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "scp-upload-file" };
+        }
+
+        private static IEnumerable<object[]> ScpUploadFileStreamExistingFileData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-upload-file" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadStreamFileDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "scp-filedoesnotexist" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadDirectoryInfoDirectoryDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-download" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadDirectoryInfoExistingFileData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "scp-download" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadDirectoryInfoExistingDirectoryData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "scp-download" };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'space \\tab\tlf*?[#~=%" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadFileInfoDirectoryDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-directorydoesnotexist", "scp-file" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadFileInfoFileDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet", "scp-filedoesnotexist" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadFileInfoExistingDirectoryData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadFileInfoExistingFileData()
+        {
+            yield return new object[] { null, "", "file 123", 0 };
+            yield return new object[] { null, "", "file 123", 1024 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
+            yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadStreamExistingDirectoryData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-test" };
+        }
+
+        private static IEnumerable<object[]> GetScpDownloadStreamExistingFileData()
+        {
+            yield return new object[] { null, "", "file 123", 0 };
+            yield return new object[] { null, "", "file 123", 1024 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
+            yield return new object[] { null, "/home/sshnet/scp test", "file 123", 1024 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/dir|&;<>()$`\"'sp\u0100ce \\tab\tlf\n*?[#~=%", "file123", 1024 };
+            yield return new object[] { RemotePathTransformation.ShellQuote, "/home/sshnet/scp-test", "file|&;<>()$`\"'sp\u0100ce \\tab\tlf*?[#~=%", 1024 };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadFileStreamDirectoryDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue289", "file123" };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadFileStreamExistingDirectoryData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue286" };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadFileInfoDirectoryDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue289", "file123" };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadFileInfoExistingDirectoryData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-issue286" };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadFileInfoExistingFileData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "/home/sshnet/scp-upload-file" };
+        }
+
+        private static IEnumerable<object[]> GetScpUploadDirectoryInfoDirectoryDoesNotExistData()
+        {
+            yield return new object[] { RemotePathTransformation.None, "scp-directorydoesnotexist" };
+        }
+
+        private static void CreateRemoteFile(ScpClient client, string remoteFile, int size)
+        {
+            var file = CreateTempFile(size);
+
+            try
+            {
+                using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
+                {
+                    client.Upload(fs, remoteFile);
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+            }
+        }
+
+        private static string GetAbsoluteRemotePath(SftpClient client, string directoryName, string fileName)
+        {
+            var absolutePath = string.Empty;
+
+            if (directoryName.Length == 0)
+            {
+                absolutePath += client.WorkingDirectory;
+            }
+            else
+            {
+                if (directoryName[0] != '/')
+                {
+                    absolutePath += client.WorkingDirectory + "/" + directoryName;
+                }
+                else
+                {
+                    absolutePath = directoryName;
+                }
+            }
+
+            return absolutePath + "/" + fileName;
+        }
+
+        private static string GetAbsoluteRemotePath(IConnectionInfoFactory connectionInfoFactory, string directoryName)
+        {
+            var absolutePath = string.Empty;
+
+            if (directoryName.Length == 0 || directoryName == ".")
+            {
+                using (var client = new SftpClient(connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    absolutePath += client.WorkingDirectory;
+                }
+            }
+            else
+            {
+                if (directoryName[0] != '/')
+                {
+                    using (var client = new SftpClient(connectionInfoFactory.Create()))
+                    {
+                        client.Connect();
+
+                        absolutePath += client.WorkingDirectory + "/" + directoryName;
+                    }
+                }
+                else
+                {
+                    absolutePath = directoryName;
+                }
+            }
+
+            return absolutePath;
+        }
+
+        private static string CreateRemoteFileHash(SftpClient client, string remotePath)
+        {
+            using (var fs = client.OpenRead(remotePath))
+            {
+                return CreateHash(fs);
+            }
+        }
+
+        private static string CombinePaths(string path1, string path2)
+        {
+            if (path1.Length == 0)
+            {
+                return path2;
+            }
+
+            if (path2.Length == 0)
+            {
+                return path1;
+            }
+
+            return path1 + "/" + path2;
+        }
+    }
+}

+ 1 - 2
src/Renci.SshNet.IntegrationTests/SftpClientTests.cs

@@ -1,7 +1,6 @@
-using Renci.SshNet;
 using Renci.SshNet.Common;
 
-namespace IntegrationTests
+namespace Renci.SshNet.IntegrationTests
 {
     /// <summary>
     /// The SFTP client integration tests

+ 6234 - 0
src/Renci.SshNet.IntegrationTests/SftpTests.cs

@@ -0,0 +1,6234 @@
+using System.Diagnostics;
+
+using Renci.SshNet.Common;
+using Renci.SshNet.IntegrationTests.Common;
+using Renci.SshNet.Sftp;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    // TODO: DeleteDirectory (fail + success
+    // TODO: DeleteFile (fail + success
+    // TODO: Delete (fail + success
+
+    [TestClass]
+    public class SftpTests : TestBase
+    {
+        private IConnectionInfoFactory _connectionInfoFactory;
+        private IConnectionInfoFactory _adminConnectionInfoFactory;
+        private IRemotePathTransformation _remotePathTransformation;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort);
+            _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort);
+            _remotePathTransformation = RemotePathTransformation.ShellQuote;
+        }
+
+#if FEATURE_MSTEST_DATATEST
+        [DataTestMethod]
+        [DynamicData(nameof(GetSftpUploadFileFileStreamData), DynamicDataSourceType.Method)]
+#else
+        [TestMethod]
+        public void Sftp_Upload_DirectoryInfo_ExistingFile()
+        {
+            foreach (var data in GetSftpUploadFileFileStreamData())
+            {
+                Sftp_UploadFile_FileStream((int) data[0]);
+            }
+        }
+#endif
+        public void Sftp_UploadFile_FileStream(int size)
+        {
+            var file = CreateTempFile(size);
+
+            using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.Delete(remoteFile);
+                }
+
+                try
+                {
+                    client.UploadFile(fs, remoteFile);
+
+                    using (var memoryStream = new MemoryStream(size))
+                    {
+                        client.DownloadFile(remoteFile, memoryStream);
+                        memoryStream.Position = 0;
+                        Assert.AreEqual(CreateFileHash(file), CreateHash(memoryStream));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.Delete(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ConnectDisconnect_Serial()
+        {
+            const int iterations = 100;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                for (var i = 1; i <= iterations; i++)
+                {
+                    client.Connect();
+                    client.Disconnect();
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ConnectDisconnect_Parallel()
+        {
+            const int iterations = 10;
+            const int threads = 20;
+
+            var startEvent = new ManualResetEvent(false);
+
+            var tasks = Enumerable.Range(1, threads).Select(i =>
+                {
+                    return Task.Factory.StartNew(() =>
+                    {
+                        using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                        {
+                            startEvent.WaitOne();
+
+                            for (var j = 0; j < iterations; j++)
+                            {
+                                client.Connect();
+                                client.Disconnect();
+                            }
+                        }
+                    });
+                }).ToArray();
+
+            startEvent.Set();
+
+            Task.WaitAll(tasks);
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile()
+        {
+            const string content = "SftpBeginUploadFile";
+
+            var expectedByteCount = (ulong) Encoding.ASCII.GetByteCount(content);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.Delete(remoteFile);
+                }
+
+                try
+                {
+                    using (var memoryStream = new MemoryStream(Encoding.ASCII.GetBytes(content)))
+                    {
+                        IAsyncResult asyncResultCallback = null;
+
+                        var asyncResult = client.BeginUploadFile(memoryStream, remoteFile, ar => asyncResultCallback = ar);
+
+                        Assert.IsTrue(asyncResult.AsyncWaitHandle.WaitOne(10000));
+
+                        // check async result
+                        var sftpUploadAsyncResult = asyncResult as SftpUploadAsyncResult;
+                        Assert.IsNotNull(sftpUploadAsyncResult);
+                        Assert.IsFalse(sftpUploadAsyncResult.IsUploadCanceled);
+                        Assert.IsTrue(sftpUploadAsyncResult.IsCompleted);
+                        Assert.IsFalse(sftpUploadAsyncResult.CompletedSynchronously);
+                        Assert.AreEqual(expectedByteCount, sftpUploadAsyncResult.UploadedBytes);
+
+                        // check async result callback
+                        var sftpUploadAsyncResultCallback = asyncResultCallback as SftpUploadAsyncResult;
+                        Assert.IsNotNull(sftpUploadAsyncResultCallback);
+                        Assert.IsFalse(sftpUploadAsyncResultCallback.IsUploadCanceled);
+                        Assert.IsTrue(sftpUploadAsyncResultCallback.IsCompleted);
+                        Assert.IsFalse(sftpUploadAsyncResultCallback.CompletedSynchronously);
+                        Assert.AreEqual(expectedByteCount, sftpUploadAsyncResultCallback.UploadedBytes);
+                    }
+
+                    // check uploaded file
+                    using (var memoryStream = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, memoryStream);
+                        memoryStream.Position = 0;
+                        var remoteContent = Encoding.ASCII.GetString(memoryStream.ToArray());
+                        Assert.AreEqual(content, remoteContent);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.Delete(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Create_ExistingFile()
+        {
+            var encoding = Encoding.UTF8;
+            var initialContent = "Gert & Ann & Lisa";
+            var newContent1 = "Sofie";
+            var newContent1Bytes = GetBytesWithPreamble(newContent1, encoding);
+            var newContent2 = "Lisa & Sofie";
+            var newContent2Bytes = GetBytesWithPreamble(newContent2, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent);
+
+                    #region Write less bytes than the current content, overwriting part of that content
+
+                    using (var fs = client.Create(remoteFile))
+                    using (var sw = new StreamWriter(fs, encoding))
+                    {
+                        sw.Write(newContent1);
+                    }
+
+                    var actualContent1 = client.ReadAllBytes(remoteFile);
+                    Assert.IsTrue(newContent1Bytes.IsEqualTo(actualContent1));
+
+                    #endregion Write less bytes than the current content, overwriting part of that content
+
+                    #region Write more bytes than the current content, overwriting and appending to that content
+
+                    using (var fs = client.Create(remoteFile))
+                    using (var sw = new StreamWriter(fs, encoding))
+                    {
+                        sw.Write(newContent2);
+                    }
+
+                    var actualContent2 = client.ReadAllBytes(remoteFile);
+                    Assert.IsTrue(newContent2Bytes.IsEqualTo(actualContent2));
+
+                    #endregion Write more bytes than the current content, overwriting and appending to that content
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Create_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                SftpFileStream fs = null;
+
+                try
+                {
+                    fs = client.Create(remoteFile);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    fs?.Dispose();
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Create_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.BufferSize = 512 * 1024;
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var imageStream = GetResourceStream("Renci.SshNet.IntegrationTests.resources.issue #70.png"))
+                    {
+                        using (var fs = client.Create(remoteFile))
+                        {
+                            byte[] buffer = new byte[Math.Min(client.BufferSize, imageStream.Length)];
+                            int bytesRead;
+
+                            while ((bytesRead = imageStream.Read(buffer, offset: 0, buffer.Length)) > 0)
+                            {
+                                fs.Write(buffer, offset: 0, bytesRead);
+                            }
+                        }
+
+                        using (var memoryStream = new MemoryStream())
+                        {
+                            client.DownloadFile(remoteFile, memoryStream);
+
+                            memoryStream.Position = 0;
+                            imageStream.Position = 0;
+
+                            Assert.AreEqual(CreateHash(imageStream), CreateHash(memoryStream));
+                        }
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllLines_NoEncoding_ExistingFile()
+        {
+            var initialContent = "\u0100ert & Ann";
+            IEnumerable<string> linesToAppend = new[] { "Forever", "&", "\u0116ver" };
+            var expectedContent = initialContent + string.Join(Environment.NewLine, linesToAppend) +
+                                  Environment.NewLine;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent);
+                    client.AppendAllLines(remoteFile, linesToAppend);
+
+                    var text = client.ReadAllText(remoteFile);
+                    Assert.AreEqual(expectedContent, text);
+
+                    // ensure we didn't write an UTF-8 BOM
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var firstByte = fs.ReadByte();
+                        Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllLines_NoEncoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            IEnumerable<string> linesToAppend = new[] { "\u0139isa", "&", "Sofie" };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.AppendAllLines(remoteFile, linesToAppend);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllLines_NoEncoding_FileDoesNotExist()
+        {
+            IEnumerable<string> linesToAppend = new[] { "\u0139isa", "&", "Sofie" };
+            var expectedContent = string.Join(Environment.NewLine, linesToAppend) + Environment.NewLine;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.AppendAllLines(remoteFile, linesToAppend);
+
+                    var text = client.ReadAllText(remoteFile);
+                    Assert.AreEqual(expectedContent, text);
+
+                    // ensure we didn't write an UTF-8 BOM
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var firstByte = fs.ReadByte();
+                        Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllText_NoEncoding_ExistingFile()
+        {
+            var initialContent = "\u0100ert & Ann";
+            var contentToAppend = "Forever&\u0116ver";
+            var expectedContent = initialContent + contentToAppend;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent);
+                    client.AppendAllText(remoteFile, contentToAppend);
+
+                    var text = client.ReadAllText(remoteFile);
+                    Assert.AreEqual(expectedContent, text);
+
+                    // ensure we didn't write an UTF-8 BOM
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var firstByte = fs.ReadByte();
+                        Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllText_NoEncoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            var contentToAppend = "Forever&\u0116ver";
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.AppendAllText(remoteFile, contentToAppend);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllText_NoEncoding_FileDoesNotExist()
+        {
+            var contentToAppend = "Forever&\u0116ver";
+            var expectedContent = contentToAppend;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.AppendAllText(remoteFile, contentToAppend);
+
+                    var text = client.ReadAllText(remoteFile);
+                    Assert.AreEqual(expectedContent, text);
+
+                    // ensure we didn't write an UTF-8 BOM
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var firstByte = fs.ReadByte();
+                        Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendText_NoEncoding_ExistingFile()
+        {
+            var initialContent = "\u0100ert & Ann";
+            var contentToAppend = "Forever&\u0116ver";
+            var expectedContent = initialContent + contentToAppend;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent);
+
+                    using (var sw = client.AppendText(remoteFile))
+                    {
+                        sw.Write(contentToAppend);
+                    }
+
+                    var text = client.ReadAllText(remoteFile);
+                    Assert.AreEqual(expectedContent, text);
+
+                    // ensure we didn't write an UTF-8 BOM
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var firstByte = fs.ReadByte();
+                        Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendText_NoEncoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                StreamWriter sw = null;
+
+                try
+                {
+                    sw = client.AppendText(remoteFile);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    sw?.Dispose();
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendText_NoEncoding_FileDoesNotExist()
+        {
+            var contentToAppend = "\u0100ert & Ann";
+            var expectedContent = contentToAppend;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var sw = client.AppendText(remoteFile))
+                    {
+                        sw.Write(contentToAppend);
+                    }
+
+                    // ensure we didn't write an UTF-8 BOM
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var firstByte = fs.ReadByte();
+                        Assert.AreEqual(Encoding.UTF8.GetBytes(expectedContent)[0], firstByte);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllLines_Encoding_ExistingFile()
+        {
+            var initialContent = "\u0100ert & Ann";
+            IEnumerable<string> linesToAppend = new[] { "Forever", "&", "\u0116ver" };
+            var expectedContent = initialContent + string.Join(Environment.NewLine, linesToAppend) +
+                                  Environment.NewLine;
+            var encoding = GetRandomEncoding();
+            var expectedBytes = GetBytesWithPreamble(expectedContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent, encoding);
+                    client.AppendAllLines(remoteFile, linesToAppend, encoding);
+
+                    var text = client.ReadAllText(remoteFile, encoding);
+                    Assert.AreEqual(expectedContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllLines_Encoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            IEnumerable<string> linesToAppend = new[] { "Forever", "&", "\u0116ver" };
+            var encoding = GetRandomEncoding();
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.AppendAllLines(remoteFile, linesToAppend, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllLines_Encoding_FileDoesNotExist()
+        {
+            IEnumerable<string> linesToAppend = new[] { "\u0139isa", "&", "Sofie" };
+            var expectedContent = string.Join(Environment.NewLine, linesToAppend) + Environment.NewLine;
+            var encoding = GetRandomEncoding();
+            var expectedBytes = GetBytesWithPreamble(expectedContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.AppendAllLines(remoteFile, linesToAppend, encoding);
+
+                    var text = client.ReadAllText(remoteFile, encoding);
+                    Assert.AreEqual(expectedContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllText_Encoding_ExistingFile()
+        {
+            var initialContent = "\u0100ert & Ann";
+            var contentToAppend = "Forever&\u0116ver";
+            var expectedContent = initialContent + contentToAppend;
+            var encoding = GetRandomEncoding();
+            var expectedBytes = GetBytesWithPreamble(expectedContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent, encoding);
+                    client.AppendAllText(remoteFile, contentToAppend, encoding);
+
+                    var text = client.ReadAllText(remoteFile, encoding);
+                    Assert.AreEqual(expectedContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllText_Encoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            const string contentToAppend = "Forever&\u0116ver";
+            var encoding = GetRandomEncoding();
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.AppendAllText(remoteFile, contentToAppend, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendAllText_Encoding_FileDoesNotExist()
+        {
+            const string contentToAppend = "Forever&\u0116ver";
+            var expectedContent = contentToAppend;
+            var encoding = GetRandomEncoding();
+            var expectedBytes = GetBytesWithPreamble(expectedContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.AppendAllText(remoteFile, contentToAppend, encoding);
+
+                    var text = client.ReadAllText(remoteFile, encoding);
+                    Assert.AreEqual(expectedContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendText_Encoding_ExistingFile()
+        {
+            const string initialContent = "\u0100ert & Ann";
+            const string contentToAppend = "Forever&\u0116ver";
+            var expectedContent = initialContent + contentToAppend;
+            var encoding = GetRandomEncoding();
+            var expectedBytes = GetBytesWithPreamble(expectedContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent, encoding);
+
+                    using (var sw = client.AppendText(remoteFile, encoding))
+                    {
+                        sw.Write(contentToAppend);
+                    }
+
+                    var text = client.ReadAllText(remoteFile, encoding);
+                    Assert.AreEqual(expectedContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendText_Encoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            var encoding = GetRandomEncoding();
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                StreamWriter sw = null;
+
+                try
+                {
+                    sw = client.AppendText(remoteFile, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    sw?.Dispose();
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_AppendText_Encoding_FileDoesNotExist()
+        {
+            var encoding = GetRandomEncoding();
+            const string contentToAppend = "\u0100ert & Ann";
+            var expectedBytes = GetBytesWithPreamble(contentToAppend, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var sw = client.AppendText(remoteFile, encoding))
+                    {
+                        sw.Write(contentToAppend);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_CreateText_NoEncoding_ExistingFile()
+        {
+            var encoding = new UTF8Encoding(false, true);
+            const string initialContent = "\u0100ert & Ann";
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+            const string newContent = "\u0116ver";
+            const string expectedContent = "\u0116ver" + " & Ann";
+            var expectedContentBytes = GetBytesWithPreamble(expectedContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent);
+
+                    using (client.CreateText(remoteFile))
+                    {
+                    }
+
+                    // verify that original content is left untouched
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
+                    }
+
+                    // write content that is less bytes than original content
+                    using (var sw = client.CreateText(remoteFile))
+                    {
+                        sw.Write(newContent);
+                    }
+
+                    // verify that original content is only partially overwritten
+                    var text = client.ReadAllText(remoteFile);
+                    Assert.AreEqual(expectedContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedContentBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_CreateText_NoEncoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                StreamWriter sw = null;
+
+                try
+                {
+                    sw = client.CreateText(remoteFile);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    sw?.Dispose();
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_CreateText_NoEncoding_FileDoesNotExist()
+        {
+            var encoding = new UTF8Encoding(false, true);
+            var initialContent = "\u0100ert & Ann";
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (client.CreateText(remoteFile))
+                    {
+                    }
+
+                    // verify that empty file was created
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var file = client.GetAttributes(remoteFile);
+                    Assert.AreEqual(0, file.Size);
+
+                    client.DeleteFile(remoteFile);
+
+                    using (var sw = client.CreateText(remoteFile))
+                    {
+                        sw.Write(initialContent);
+                    }
+
+                    // verify that content is written to file
+                    var text = client.ReadAllText(remoteFile);
+                    Assert.AreEqual(initialContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_CreateText_Encoding_ExistingFile()
+        {
+            var encoding = GetRandomEncoding();
+            var initialContent = "\u0100ert & Ann";
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+            var newContent = "\u0116ver";
+            var expectedContent = "\u0116ver" + " & Ann";
+            var expectedContentBytes = GetBytesWithPreamble(expectedContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent, encoding);
+
+                    using (client.CreateText(remoteFile))
+                    {
+                    }
+
+                    // verify that original content is left untouched
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
+                    }
+
+                    // write content that is less bytes than original content
+                    using (var sw = client.CreateText(remoteFile, encoding))
+                    {
+                        sw.Write(newContent);
+                    }
+
+                    // verify that original content is only partially overwritten
+                    var text = client.ReadAllText(remoteFile, encoding);
+                    Assert.AreEqual(expectedContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedContentBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_CreateText_Encoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            var encoding = GetRandomEncoding();
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                StreamWriter sw = null;
+
+                try
+                {
+                    sw = client.CreateText(remoteFile, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    sw?.Dispose();
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_CreateText_Encoding_FileDoesNotExist()
+        {
+            var encoding = GetRandomEncoding();
+            var initialContent = "\u0100ert & Ann";
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (client.CreateText(remoteFile, encoding))
+                    {
+                    }
+
+                    // verify that file containing only preamble was created
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var file = client.GetAttributes(remoteFile);
+                    Assert.AreEqual(encoding.GetPreamble().Length, file.Size);
+
+                    client.DeleteFile(remoteFile);
+
+                    using (var sw = client.CreateText(remoteFile, encoding))
+                    {
+                        sw.Write(initialContent);
+                    }
+
+                    // verify that content is written to file
+                    var text = client.ReadAllText(remoteFile, encoding);
+                    Assert.AreEqual(initialContent, text);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    { 
+                            client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_DownloadFile_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                using (var ms = new MemoryStream())
+                {
+                    try
+                    {
+                        client.DownloadFile(remoteFile, ms);
+                        Assert.Fail();
+                    }
+                    catch (SftpPathNotFoundException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual("No such file", ex.Message);
+
+                        // ensure file was not created by us
+                        Assert.IsFalse(client.Exists(remoteFile));
+                    }
+                    finally
+                    {
+                        if (client.Exists(remoteFile))
+                        {
+                            client.DeleteFile(remoteFile);
+                        }
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllBytes_ExistingFile()
+        {
+            var encoding = GetRandomEncoding();
+            var content = "\u0100ert & Ann";
+            var contentBytes = GetBytesWithPreamble(content, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, content, encoding);
+
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    Assert.IsTrue(contentBytes.IsEqualTo(actualBytes));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    { 
+                            client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllBytes_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.ReadAllBytes(remoteFile);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+
+                    // ensure file was not created by us
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllLines_NoEncoding_ExistingFile()
+        {
+            var encoding = new UTF8Encoding(false, true);
+            var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" };
+            var linesBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, lines) + Environment.NewLine,
+                                                  encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var sw = client.AppendText(remoteFile))
+                    {
+                        for (var i = 0; i < lines.Length; i++)
+                        {
+                            sw.WriteLine(lines[i]);
+                        }
+                    }
+
+                    var actualLines = client.ReadAllLines(remoteFile);
+                    Assert.IsNotNull(actualLines);
+                    Assert.AreEqual(lines.Length, actualLines.Length);
+
+                    for (var i = 0; i < lines.Length; i++)
+                    {
+                        Assert.AreEqual(lines[i], actualLines[i]);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(linesBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllLines_NoEncoding_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.ReadAllLines(remoteFile);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+
+                    // ensure file was not created by us
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllLines_Encoding_ExistingFile()
+        {
+            var encoding = GetRandomEncoding();
+            var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" };
+            var linesBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, lines) + Environment.NewLine,
+                                                  encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var sw = client.AppendText(remoteFile, encoding))
+                    {
+                        for (var i = 0; i < lines.Length; i++)
+                        {
+                            sw.WriteLine(lines[i]);
+                        }
+                    }
+
+                    var actualLines = client.ReadAllLines(remoteFile, encoding);
+                    Assert.IsNotNull(actualLines);
+                    Assert.AreEqual(lines.Length, actualLines.Length);
+
+                    for (var i = 0; i < lines.Length; i++)
+                    {
+                        Assert.AreEqual(lines[i], actualLines[i]);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(linesBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllLines_Encoding_FileDoesNotExist()
+        {
+            var encoding = GetRandomEncoding();
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.ReadAllLines(remoteFile, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+
+                    // ensure file was not created by us
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllText_NoEncoding_ExistingFile()
+        {
+            var encoding = new UTF8Encoding(false, true);
+            var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" };
+            var expectedText = string.Join(Environment.NewLine, lines) + Environment.NewLine;
+            var expectedBytes = GetBytesWithPreamble(expectedText, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var sw = client.AppendText(remoteFile))
+                    {
+                        for (var i = 0; i < lines.Length; i++)
+                        {
+                            sw.WriteLine(lines[i]);
+                        }
+                    }
+
+                    var actualText = client.ReadAllText(remoteFile);
+                    Assert.AreEqual(actualText, expectedText);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllText_NoEncoding_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.ReadAllText(remoteFile);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+
+                    // ensure file was not created by us
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllText_Encoding_ExistingFile()
+        {
+            var encoding = GetRandomEncoding();
+            var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" };
+            var expectedText = string.Join(Environment.NewLine, lines) + Environment.NewLine;
+            var expectedBytes = GetBytesWithPreamble(expectedText, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var sw = client.AppendText(remoteFile, encoding))
+                    {
+                        for (var i = 0; i < lines.Length; i++)
+                        {
+                            sw.WriteLine(lines[i]);
+                        }
+                    }
+
+                    var actualText = client.ReadAllText(remoteFile, encoding);
+                    Assert.AreEqual(expectedText, actualText);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadAllText_Encoding_FileDoesNotExist()
+        {
+            var encoding = GetRandomEncoding();
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.ReadAllText(remoteFile, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+
+                    // ensure file was not created by us
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadLines_NoEncoding_ExistingFile()
+        {
+            var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var sw = client.AppendText(remoteFile))
+                    {
+                        for (var i = 0; i < lines.Length; i++)
+                        {
+                            sw.WriteLine(lines[i]);
+                        }
+                    }
+
+                    var actualLines = client.ReadLines(remoteFile);
+                    Assert.IsNotNull(actualLines);
+
+                    var actualLinesEnum = actualLines.GetEnumerator();
+                    for (var i = 0; i < lines.Length; i++)
+                    {
+                        Assert.IsTrue(actualLinesEnum.MoveNext());
+                        var actualLine = actualLinesEnum.Current;
+                        Assert.AreEqual(lines[i], actualLine);
+                    }
+
+                    Assert.IsFalse(actualLinesEnum.MoveNext());
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadLines_NoEncoding_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.ReadLines(remoteFile);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+
+                    // ensure file was not created by us
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadLines_Encoding_ExistingFile()
+        {
+            var encoding = GetRandomEncoding();
+            var lines = new[] { "\u0100ert & Ann", "Forever", "&", "\u0116ver" };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var sw = client.AppendText(remoteFile, encoding))
+                    {
+                        for (var i = 0; i < lines.Length; i++)
+                        {
+                            sw.WriteLine(lines[i]);
+                        }
+                    }
+
+                    var actualLines = client.ReadLines(remoteFile, encoding);
+                    Assert.IsNotNull(actualLines);
+
+                    using (var actualLinesEnum = actualLines.GetEnumerator())
+                    {
+                        for (var i = 0; i < lines.Length; i++)
+                        {
+                            Assert.IsTrue(actualLinesEnum.MoveNext());
+
+                            var actualLine = actualLinesEnum.Current;
+                            Assert.AreEqual(lines[i], actualLine);
+                        }
+
+                        Assert.IsFalse(actualLinesEnum.MoveNext());
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ReadLines_Encoding_FileDoesNotExist()
+        {
+            var encoding = GetRandomEncoding();
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.ReadLines(remoteFile, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+
+                    // ensure file was not created by us
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllBytes_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            var content = GenerateRandom(size: 5);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllBytes(remoteFile, content);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllBytes_ExistingFile()
+        {
+            var initialContent = GenerateRandom(size: 13);
+            var newContent1 = GenerateRandom(size: 5);
+            var expectedContent1 = new ArrayBuilder<byte>().Add(newContent1)
+                                                           .Add(initialContent, newContent1.Length, initialContent.Length - newContent1.Length)
+                                                           .Build();
+            var newContent2 = GenerateRandom(size: 50000);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var fs = client.Create(remoteFile))
+                    {
+                        fs.Write(initialContent, offset: 0, initialContent.Length);
+                    }
+
+                    #region Write less bytes than the current content, overwriting part of that content
+
+                    client.WriteAllBytes(remoteFile, newContent1);
+
+                    var actualContent1 = client.ReadAllBytes(remoteFile);
+                    Assert.IsTrue(expectedContent1.IsEqualTo(actualContent1));
+
+                    #endregion Write less bytes than the initial content, overwriting part of that content
+
+                    #region Write more bytes than the current content, overwriting and appending to that content
+
+                    client.WriteAllBytes(remoteFile, newContent2);
+
+                    var actualContent2 = client.ReadAllBytes(remoteFile);
+                    Assert.IsTrue(newContent2.IsEqualTo(actualContent2));
+
+                    #endregion Write less bytes than the initial content, overwriting part of that content
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllBytes_FileDoesNotExist()
+        {
+            var content = GenerateRandom(size: 13);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllBytes(remoteFile, content);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(content.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_IEnumerable_NoEncoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            IEnumerable<string> linesToWrite = new[] { "Forever", "&", "\u0116ver" };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllLines(remoteFile, linesToWrite);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_IEnumerable_NoEncoding_ExistingFile()
+        {
+            var encoding = new UTF8Encoding(false, true);
+            var initialContent = "\u0100ert & Ann Forever & Ever Lisa & Sofie";
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+            IEnumerable<string> linesToWrite1 = new[] { "Forever", "&", "\u0116ver" };
+            var linesToWrite1Bytes =
+                GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite1) + Environment.NewLine, encoding);
+            var expectedBytes1 = new ArrayBuilder<byte>().Add(linesToWrite1Bytes)
+                                                         .Add(initialContentBytes,
+                                                              linesToWrite1Bytes.Length,
+                                                              initialContentBytes.Length - linesToWrite1Bytes.Length)
+                                                         .Build();
+            IEnumerable<string> linesToWrite2 = new[] { "Forever", "&", "\u0116ver", "Gert & Ann", "Lisa + Sofie" };
+            var linesToWrite2Bytes =
+                GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite2) + Environment.NewLine, encoding);
+            var expectedBytes2 = linesToWrite2Bytes;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    // create initial content
+                    client.WriteAllText(remoteFile, initialContent);
+
+                    #region Write less bytes than the current content, overwriting part of that content
+
+                    client.WriteAllLines(remoteFile, linesToWrite1);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write less bytes than the current content, overwriting part of that content
+
+                    #region Write more bytes than the current content, overwriting and appending to that content
+
+                    client.WriteAllLines(remoteFile, linesToWrite2);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write more bytes than the current content, overwriting and appending to that content
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_IEnumerable_NoEncoding_FileDoesNotExist()
+        {
+            var encoding = new UTF8Encoding(false, true);
+            IEnumerable<string> linesToWrite = new[] { "\u0139isa", "&", "Sofie" };
+            var linesToWriteBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite) + Environment.NewLine, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllLines(remoteFile, linesToWrite);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_IEnumerable_Encoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            var encoding = GetRandomEncoding();
+            IEnumerable<string> linesToWrite = new[] { "Forever", "&", "\u0116ver" };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllLines(remoteFile, linesToWrite, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_IEnumerable_Encoding_ExistingFile()
+        {
+            var encoding = GetRandomEncoding();
+            const string initialContent = "\u0100ert & Ann Forever & Ever Lisa & Sofie";
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+            IEnumerable<string> linesToWrite1 = new[] { "Forever", "&", "\u0116ver" };
+            var linesToWrite1Bytes =
+                GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite1) + Environment.NewLine, encoding);
+            var expectedBytes1 = new ArrayBuilder<byte>().Add(linesToWrite1Bytes)
+                                                         .Add(initialContentBytes,
+                                                              linesToWrite1Bytes.Length,
+                                                              initialContentBytes.Length - linesToWrite1Bytes.Length)
+                                                         .Build();
+            IEnumerable<string> linesToWrite2 = new[] { "Forever", "&", "\u0116ver", "Gert & Ann", "Lisa + Sofie" };
+            var linesToWrite2Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite2) + Environment.NewLine, encoding);
+            var expectedBytes2 = linesToWrite2Bytes;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    // create initial content
+                    client.WriteAllText(remoteFile, initialContent, encoding);
+
+                    #region Write less bytes than the current content, overwriting part of that content
+
+                    client.WriteAllLines(remoteFile, linesToWrite1, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write less bytes than the current content, overwriting part of that content
+
+                    #region Write more bytes than the current content, overwriting and appending to that content
+
+                    client.WriteAllLines(remoteFile, linesToWrite2, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write more bytes than the current content, overwriting and appending to that content
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_IEnumerable_Encoding_FileDoesNotExist()
+        {
+            var encoding = GetRandomEncoding();
+            IEnumerable<string> linesToWrite = new[] { "\u0139isa", "&", "Sofie" };
+            var linesToWriteBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite) + Environment.NewLine, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllLines(remoteFile, linesToWrite, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_Array_NoEncoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            var linesToWrite = new[] { "Forever", "&", "\u0116ver" };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllLines(remoteFile, linesToWrite);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_Array_NoEncoding_ExistingFile()
+        {
+            var encoding = new UTF8Encoding(false, true);
+            const string initialContent = "\u0100ert & Ann Forever & Ever Lisa & Sofie";
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+            var linesToWrite1 = new[] { "Forever", "&", "\u0116ver" };
+            var linesToWrite1Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite1) + Environment.NewLine, encoding);
+            var expectedBytes1 = new ArrayBuilder<byte>().Add(linesToWrite1Bytes)
+                                                         .Add(initialContentBytes, linesToWrite1Bytes.Length, initialContentBytes.Length - linesToWrite1Bytes.Length)
+                                                         .Build();
+            var linesToWrite2 = new[] { "Forever", "&", "\u0116ver", "Gert & Ann", "Lisa + Sofie" };
+            var linesToWrite2Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite2) + Environment.NewLine, encoding);
+            var expectedBytes2 = linesToWrite2Bytes;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    // create initial content
+                    client.WriteAllText(remoteFile, initialContent);
+
+                    #region Write less bytes than the current content, overwriting part of that content
+
+                    client.WriteAllLines(remoteFile, linesToWrite1);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write less bytes than the current content, overwriting part of that content
+
+                    #region Write more bytes than the current content, overwriting and appending to that content
+
+                    client.WriteAllLines(remoteFile, linesToWrite2);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write more bytes than the current content, overwriting and appending to that content
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_Array_NoEncoding_FileDoesNotExist()
+        {
+            var encoding = new UTF8Encoding(false, true);
+            var linesToWrite = new[] { "\u0139isa", "&", "Sofie" };
+            var linesToWriteBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite) + Environment.NewLine, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllLines(remoteFile, linesToWrite);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_Array_Encoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            var encoding = GetRandomEncoding();
+            var linesToWrite = new[] { "Forever", "&", "\u0116ver" };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllLines(remoteFile, linesToWrite, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_Array_Encoding_ExistingFile()
+        {
+            const string initialContent = "\u0100ert & Ann Forever & Ever Lisa & Sofie";
+
+            var encoding = GetRandomEncoding();
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+            var linesToWrite1 = new[] { "Forever", "&", "\u0116ver" };
+            var linesToWrite1Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite1) + Environment.NewLine, encoding);
+            var expectedBytes1 = new ArrayBuilder<byte>().Add(linesToWrite1Bytes)
+                                                         .Add(initialContentBytes, linesToWrite1Bytes.Length, initialContentBytes.Length - linesToWrite1Bytes.Length)
+                                                         .Build();
+            var linesToWrite2 = new[] { "Forever", "&", "\u0116ver", "Gert & Ann", "Lisa + Sofie" };
+            var linesToWrite2Bytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite2) + Environment.NewLine, encoding);
+            var expectedBytes2 = linesToWrite2Bytes;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    // create initial content
+                    client.WriteAllText(remoteFile, initialContent, encoding);
+
+                    #region Write less bytes than the current content, overwriting part of that content
+
+                    client.WriteAllLines(remoteFile, linesToWrite1, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write less bytes than the current content, overwriting part of that content
+
+                    #region Write more bytes than the current content, overwriting and appending to that content
+
+                    client.WriteAllLines(remoteFile, linesToWrite2, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write more bytes than the current content, overwriting and appending to that content
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllLines_Array_Encoding_FileDoesNotExist()
+        {
+            var encoding = GetRandomEncoding();
+            var linesToWrite = new[] { "\u0139isa", "&", "Sofie" };
+            var linesToWriteBytes = GetBytesWithPreamble(string.Join(Environment.NewLine, linesToWrite) + Environment.NewLine, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllLines(remoteFile, linesToWrite, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllText_NoEncoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie";
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllText_NoEncoding_ExistingFile()
+        {
+            const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie";
+            const string newContent1 = "For\u0116ver & Ever";
+
+            var encoding = new UTF8Encoding(false, true);
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+            var newContent1Bytes = GetBytesWithPreamble(newContent1, encoding);
+            var expectedBytes1 = new ArrayBuilder<byte>().Add(newContent1Bytes)
+                                                         .Add(initialContentBytes, newContent1Bytes.Length, initialContentBytes.Length - newContent1Bytes.Length)
+                                                         .Build();
+            var newContent2 = "Sofie & Lisa For\u0116ver & Ever with \u0100ert & Ann";
+            var newContent2Bytes = GetBytesWithPreamble(newContent2, encoding);
+            var expectedBytes2 = newContent2Bytes;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent);
+
+                    #region Write less bytes than the current content, overwriting part of that content
+
+                    client.WriteAllText(remoteFile, newContent1);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write less bytes than the current content, overwriting part of that content
+
+                    #region Write more bytes than the current content, overwriting and appending to that content
+
+                    client.WriteAllText(remoteFile, newContent2);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write more bytes than the current content, overwriting and appending to that content
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllText_NoEncoding_FileDoesNotExist()
+        {
+            const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie";
+
+            var encoding = new UTF8Encoding(false, true);
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllText_Encoding_DirectoryDoesNotExist()
+        {
+            const string remoteFile = "/home/sshnet/directorydoesnotexist/test";
+
+            var encoding = GetRandomEncoding();
+            const string content = "For\u0116ver & Ever";
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, content, encoding);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllText_Encoding_ExistingFile()
+        {
+            const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie";
+            const string newContent1 = "For\u0116ver & Ever";
+            const string newContent2 = "Sofie & Lisa For\u0116ver & Ever with \u0100ert & Ann";
+
+            var encoding = GetRandomEncoding();
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+            var newContent1Bytes = GetBytesWithPreamble(newContent1, encoding);
+            var expectedBytes1 = new ArrayBuilder<byte>().Add(newContent1Bytes)
+                                                         .Add(initialContentBytes, newContent1Bytes.Length, initialContentBytes.Length - newContent1Bytes.Length)
+                                                         .Build();
+            var newContent2Bytes = GetBytesWithPreamble(newContent2, encoding);
+            var expectedBytes2 = newContent2Bytes;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent, encoding);
+
+                    #region Write less bytes than the current content, overwriting part of that content
+
+                    client.WriteAllText(remoteFile, newContent1, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write less bytes than the current content, overwriting part of that content
+
+                    #region Write more bytes than the current content, overwriting and appending to that content
+
+                    client.WriteAllText(remoteFile, newContent2, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
+                    }
+
+                    #endregion Write more bytes than the current content, overwriting and appending to that content
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_WriteAllText_Encoding_FileDoesNotExist()
+        {
+            const string initialContent = "\u0100ert & Ann Forever & \u0116ver Lisa & Sofie";
+
+            var encoding = GetRandomEncoding();
+            var initialContentBytes = GetBytesWithPreamble(initialContent, encoding);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, initialContent, encoding);
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var actualBytes = new byte[fs.Length];
+                        fs.Read(actualBytes, offset: 0, actualBytes.Length);
+                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginDownloadFile_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var ms = new MemoryStream())
+                    {
+                        var asyncResult = client.BeginDownloadFile(remoteFile, ms);
+                        try
+                        {
+                            client.EndDownloadFile(asyncResult);
+                            Assert.Fail();
+                        }
+                        catch (SftpPathNotFoundException ex)
+                        {
+                            Assert.IsNull(ex.InnerException);
+                            Assert.AreEqual("No such file", ex.Message);
+
+                            // ensure file was not created by us
+                            Assert.IsFalse(client.Exists(remoteFile));
+                        }
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginListDirectory_DirectoryDoesNotExist()
+        {
+            const string remoteDirectory = "/home/sshnet/test123";
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    command.Execute();
+                }
+            }
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var asyncResult = client.BeginListDirectory(remoteDirectory, null, null);
+                try
+                {
+                    client.EndListDirectory(asyncResult);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException ex)
+                {
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("No such file", ex.Message);
+
+                    // ensure directory was not created by us
+                    Assert.IsFalse(client.Exists(remoteDirectory));
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPath_DirectoryDoesNotExist()
+        {
+            const int size = 50 * 1024 * 1024;
+            const string remoteDirectory = "/home/sshnet/test123";
+            const string remoteFile = remoteDirectory + "/test";
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    command.Execute();
+                }
+            }
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var memoryStream = CreateMemoryStream(size);
+                memoryStream.Position = 0;
+
+                var asyncResult = client.BeginUploadFile(memoryStream, remoteFile);
+                try
+                {
+                    client.EndUploadFile(asyncResult);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException)
+                {
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPath_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    var uploadMemoryStream = new MemoryStream();
+                    var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8);
+                    sw.Write("Gert & Ann");
+                    sw.Flush();
+                    uploadMemoryStream.Position = 0;
+
+                    var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile);
+                    client.EndUploadFile(asyncResult);
+
+                    using (var downloadMemoryStream = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloadMemoryStream);
+
+                        downloadMemoryStream.Position = 0;
+
+                        using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8))
+                        {
+                            var content = sr.ReadToEnd();
+                            Assert.AreEqual("Gert & Ann", content);
+                        }
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPath_ExistingFile()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, "Gert & Ann & Lisa");
+
+                    var uploadMemoryStream = new MemoryStream();
+                    var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8);
+                    sw.Write("Ann & Gert");
+                    sw.Flush();
+                    uploadMemoryStream.Position = 0;
+
+                    var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile);
+                    client.EndUploadFile(asyncResult);
+
+                    using (var downloadMemoryStream = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloadMemoryStream);
+
+                        downloadMemoryStream.Position = 0;
+
+                        using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8))
+                        {
+                            var content = sr.ReadToEnd();
+                            Assert.AreEqual("Ann & Gert", content);
+                        }
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsFalse_DirectoryDoesNotExist()
+        {
+            const int size = 50 * 1024 * 1024;
+            const string remoteDirectory = "/home/sshnet/test123";
+            const string remoteFile = remoteDirectory + "/test";
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    command.Execute();
+                }
+            }
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var memoryStream = CreateMemoryStream(size);
+                memoryStream.Position = 0;
+
+                var asyncResult = client.BeginUploadFile(memoryStream, remoteFile, false, null, null);
+                try
+                {
+                    client.EndUploadFile(asyncResult);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException)
+                {
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsFalse_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var uploadMemoryStream = new MemoryStream())
+                    using (var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8))
+                    {
+                        sw.Write("Gert & Ann");
+                        sw.Flush();
+                        uploadMemoryStream.Position = 0;
+
+                        var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile, false, null, null);
+                        client.EndUploadFile(asyncResult);
+                    }
+
+                    using (var downloadMemoryStream = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloadMemoryStream);
+
+                        downloadMemoryStream.Position = 0;
+
+                        using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8))
+                        {
+                            var content = sr.ReadToEnd();
+                            Assert.AreEqual("Gert & Ann", content);
+                        }
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsFalse_ExistingFile()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, "Gert & Ann & Lisa");
+
+                    var uploadMemoryStream = new MemoryStream();
+                    var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8);
+                    sw.Write("Ann & Gert");
+                    sw.Flush();
+                    uploadMemoryStream.Position = 0;
+
+                    var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile, false, null, null);
+
+                    try
+                    {
+                        client.EndUploadFile(asyncResult);
+                        Assert.Fail();
+                    }
+                    catch (SshException ex)
+                    {
+                        Assert.AreEqual(typeof(SshException), ex.GetType());
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual("Failure", ex.Message);
+                    }
+                }
+                finally
+                {
+
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsTrue_DirectoryDoesNotExist()
+        {
+            const int size = 50 * 1024 * 1024;
+            const string remoteDirectory = "/home/sshnet/test123";
+            const string remoteFile = remoteDirectory + "/test";
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    command.Execute();
+                }
+            }
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var memoryStream = CreateMemoryStream(size);
+                memoryStream.Position = 0;
+
+                var asyncResult = client.BeginUploadFile(memoryStream, remoteFile, true, null, null);
+                try
+                {
+                    client.EndUploadFile(asyncResult);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException)
+                {
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsTrue_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var uploadMemoryStream = new MemoryStream())
+                    using (var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8))
+                    {
+                        sw.Write("Gert & Ann");
+                        sw.Flush();
+                        uploadMemoryStream.Position = 0;
+
+                        var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile, true, null, null);
+                        client.EndUploadFile(asyncResult);
+                    }
+
+                    using (var downloadMemoryStream = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloadMemoryStream);
+
+                        downloadMemoryStream.Position = 0;
+
+                        using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8))
+                        {
+                            var content = sr.ReadToEnd();
+                            Assert.AreEqual("Gert & Ann", content);
+                        }
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsTrue_ExistingFile()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllText(remoteFile, "Gert & Ann & Lisa");
+
+                    using (var uploadMemoryStream = new MemoryStream())
+                    using (var sw = new StreamWriter(uploadMemoryStream, Encoding.UTF8))
+                    {
+                        sw.Write("Ann & Gert");
+                        sw.Flush();
+                        uploadMemoryStream.Position = 0;
+
+                        var asyncResult = client.BeginUploadFile(uploadMemoryStream, remoteFile, true, null, null);
+                        client.EndUploadFile(asyncResult);
+                    }
+
+                    using (var downloadMemoryStream = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloadMemoryStream);
+
+                        downloadMemoryStream.Position = 0;
+
+                        using (var sr = new StreamReader(downloadMemoryStream, Encoding.UTF8))
+                        {
+                            var content = sr.ReadToEnd();
+                            Assert.AreEqual("Ann & Gert", content);
+                        }
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_UploadAndDownloadBigFile()
+        {
+            const int size = 50 * 1024 * 1024;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.Delete(remoteFile);
+                }
+
+                try
+                {
+                    var memoryStream = CreateMemoryStream(size);
+                    memoryStream.Position = 0;
+
+                    client.UploadFile(memoryStream, remoteFile);
+
+                    var stopwatch = new Stopwatch();
+                    stopwatch.Start();
+
+                    // check uploaded file
+                    memoryStream = new MemoryStream();
+                    client.DownloadFile(remoteFile, memoryStream);
+
+                    Assert.AreEqual(size, memoryStream.Length);
+
+                    stopwatch.Stop();
+
+                    Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
+                    Console.WriteLine(@"Transfer speed: {0:N2} KB/s",
+                                      CalculateTransferSpeed(memoryStream.Length, stopwatch.ElapsedMilliseconds));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.Delete(remoteFile);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Issue 1672
+        /// </summary>
+        [TestMethod]
+        public void Sftp_CurrentWorkingDirectory()
+        {
+            const string homeDirectory = "/home/sshnet";
+            const string otherDirectory = homeDirectory + "/dir";
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(otherDirectory))
+                {
+                    client.DeleteDirectory(otherDirectory);
+                }
+
+                try
+                {
+                    client.CreateDirectory(otherDirectory);
+                    client.ChangeDirectory(otherDirectory);
+
+                    using (var s = CreateStreamWithContent("A"))
+                    {
+                        client.UploadFile(s, "a.txt");
+                    }
+
+                    using (var s = new MemoryStream())
+                    {
+                        client.DownloadFile("a.txt", s);
+                        s.Position = 0;
+
+                        var content = Encoding.ASCII.GetString(s.ToArray());
+                        Assert.AreEqual("A", content);
+                    }
+
+                    Assert.IsTrue(client.Exists(otherDirectory + "/a.txt"));
+                    client.DeleteFile("a.txt");
+                    Assert.IsFalse(client.Exists(otherDirectory + "/a.txt"));
+                    client.DeleteDirectory(".");
+                    Assert.IsFalse(client.Exists(otherDirectory));
+                }
+                finally
+                {
+                    if (client.Exists(otherDirectory))
+                    {
+                        client.DeleteDirectory(otherDirectory);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Exists()
+        {
+            const string remoteHome = "/home/sshnet";
+
+            #region Setup
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                #region Clean-up
+
+                using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/DoesNotExist"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/symlink.to.directory.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/directory.exists"}")
+                )
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/symlink.to.file.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"rm -f {remoteHome + "/file.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                #endregion Clean-up
+
+                #region Setup
+
+                using (var command = client.CreateCommand($"touch {remoteHome + "/file.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"mkdir {remoteHome + "/directory.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"ln -s {remoteHome + "/file.exists"} {remoteHome + "/symlink.to.file.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"ln -s {remoteHome + "/directory.exists"} {remoteHome + "/symlink.to.directory.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                #endregion Setup
+            }
+
+            #endregion Setup
+
+            #region Assert
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                Assert.IsFalse(client.Exists(remoteHome + "/DoesNotExist"));
+                Assert.IsTrue(client.Exists(remoteHome + "/file.exists"));
+                Assert.IsTrue(client.Exists(remoteHome + "/symlink.to.file.exists"));
+                Assert.IsTrue(client.Exists(remoteHome + "/directory.exists"));
+                Assert.IsTrue(client.Exists(remoteHome + "/symlink.to.directory.exists"));
+            }
+
+            #endregion Assert
+
+            #region Teardown
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/DoesNotExist"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/symlink.to.directory.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/directory.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"rm -Rf {remoteHome + "/symlink.to.file.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+
+                using (var command = client.CreateCommand($"rm -f {remoteHome + "/file.exists"}"))
+                {
+                    command.Execute();
+                    Assert.AreEqual(0, command.ExitStatus, command.Error);
+                }
+            }
+
+            #endregion Teardown
+        }
+
+        [TestMethod]
+        public void Sftp_ListDirectory()
+        {
+            const string remoteDirectory = "/home/sshnet/test123";
+
+            try
+            {
+                using (var client = new SshClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+                    client.RunCommand($@"rm -Rf ""{remoteDirectory}""");
+                    client.RunCommand($@"mkdir -p ""{remoteDirectory}""");
+                    client.RunCommand($@"mkdir -p ""{remoteDirectory}/sub""");
+                    client.RunCommand($@"touch ""{remoteDirectory}/file1""");
+                    client.RunCommand($@"touch ""{remoteDirectory}/file2""");
+                }
+
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    client.ChangeDirectory(remoteDirectory);
+
+                    var directoryContent = client.ListDirectory(".").OrderBy(p => p.Name).ToList();
+                    Assert.AreEqual(5, directoryContent.Count);
+
+                    Assert.AreEqual(".", directoryContent[0].Name);
+                    Assert.AreEqual($"{remoteDirectory}/.", directoryContent[0].FullName);
+                    Assert.IsTrue(directoryContent[0].IsDirectory);
+
+                    Assert.AreEqual("..", directoryContent[1].Name);
+                    Assert.AreEqual($"{remoteDirectory}/..", directoryContent[1].FullName);
+                    Assert.IsTrue(directoryContent[1].IsDirectory);
+
+                    Assert.AreEqual("file1", directoryContent[2].Name);
+                    Assert.AreEqual($"{remoteDirectory}/file1", directoryContent[2].FullName);
+                    Assert.IsFalse(directoryContent[2].IsDirectory);
+
+                    Assert.AreEqual("file2", directoryContent[3].Name);
+                    Assert.AreEqual($"{remoteDirectory}/file2", directoryContent[3].FullName);
+                    Assert.IsFalse(directoryContent[3].IsDirectory);
+
+                    Assert.AreEqual("sub", directoryContent[4].Name);
+                    Assert.AreEqual($"{remoteDirectory}/sub", directoryContent[4].FullName);
+                    Assert.IsTrue(directoryContent[4].IsDirectory);
+                }
+            }
+            finally
+            {
+                using (var client = new SshClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                    {
+                        command.Execute();
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ChangeDirectory_DirectoryDoesNotExist()
+        {
+            const string remoteDirectory = "/home/sshnet/test123";
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    command.Execute();
+                }
+            }
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                try
+                {
+                    client.ChangeDirectory(remoteDirectory);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException)
+                {
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_ChangeDirectory_DirectoryExists()
+        {
+            const string remoteDirectory = "/home/sshnet/test123";
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    command.Execute();
+                }
+
+                using (var command = client.CreateCommand("mkdir -p " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    command.Execute();
+                }
+            }
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    client.ChangeDirectory(remoteDirectory);
+
+                    Assert.AreEqual(remoteDirectory, client.WorkingDirectory);
+
+                    using (var uploadStream = CreateMemoryStream(100))
+                    {
+                        uploadStream.Position = 0;
+
+                        client.UploadFile(uploadStream, "gert.txt");
+
+                        uploadStream.Position = 0;
+
+                        using (var downloadStream = client.OpenRead(remoteDirectory + "/gert.txt"))
+                        {
+                            Assert.AreEqual(CreateHash(uploadStream), CreateHash(downloadStream));
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                using (var client = new SshClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                    {
+                        command.Execute();
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_DownloadFile_MemoryStream()
+        {
+            const int fileSize = 500 * 1024;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                SftpCreateRemoteFile(client, remoteFile, fileSize);
+
+                try
+                {
+                    using (var memoryStream = new MemoryStream())
+                    {
+                        var stopwatch = new Stopwatch();
+                        stopwatch.Start();
+
+                        client.DownloadFile(remoteFile, memoryStream);
+                        stopwatch.Stop();
+
+                        var transferSpeed = CalculateTransferSpeed(memoryStream.Length, stopwatch.ElapsedMilliseconds);
+                        Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
+                        Console.WriteLine(@"Transfer speed: {0:N2} KB/s", transferSpeed);
+
+                        Assert.AreEqual(fileSize, memoryStream.Length);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_SubsystemExecution_Failed()
+        {
+            var remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig();
+
+            // Disable SFTP subsystem
+            remoteSshdConfig.ClearSubsystems()
+                            .Update()
+                            .Restart();
+
+            var remoteSshdReconfiguredToDefaultState = false;
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    try
+                    {
+                        client.Connect();
+                        Assert.Fail("Establishing SFTP connection should have failed.");
+                    }
+                    catch (SshException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual("Subsystem 'sftp' could not be executed.", ex.Message);
+                    }
+
+                    // Re-enable SFTP subsystem
+                    remoteSshdConfig.Reset();
+
+                    remoteSshdReconfiguredToDefaultState = true;
+
+                    // ensure we can reconnect the same SftpClient instance
+                    client.Connect();
+                    // ensure SFTP session is correctly established
+                    Assert.IsTrue(client.Exists("."));
+                }
+            }
+            finally
+            {
+                if (!remoteSshdReconfiguredToDefaultState)
+                {
+                    remoteSshdConfig.Reset();
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_SftpFileStream_ReadAndWrite()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var s = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write))
+                    {
+                        s.Write(new byte[] { 5, 4, 3, 2, 1 }, 1, 3);
+                    }
+
+                    // switch from read to write mode
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite))
+                    {
+                        Assert.AreEqual(4, s.ReadByte());
+                        Assert.AreEqual(3, s.ReadByte());
+
+                        Assert.AreEqual(2, s.Position);
+
+                        s.WriteByte(7);
+                        s.Write(new byte[] { 8, 9, 10, 11, 12 }, 1, 3);
+
+                        Assert.AreEqual(6, s.Position);
+                    }
+
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Read))
+                    {
+                        Assert.AreEqual(6, s.Length);
+
+                        var buffer = new byte[s.Length];
+                        Assert.AreEqual(6, s.Read(buffer, offset: 0, buffer.Length));
+
+                        CollectionAssert.AreEqual(new byte[] { 4, 3, 7, 9, 10, 11 }, buffer);
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, s.ReadByte());
+                    }
+
+                    // switch from read to write mode, and back to read mode and finally
+                    // append a byte
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite))
+                    {
+                        Assert.AreEqual(4, s.ReadByte());
+                        Assert.AreEqual(3, s.ReadByte());
+                        Assert.AreEqual(7, s.ReadByte());
+
+                        s.Write(new byte[] { 0, 1, 6, 4 }, 1, 2);
+
+                        Assert.AreEqual(11, s.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, s.ReadByte());
+
+                        s.WriteByte(12);
+                    }
+
+                    // switch from write to read mode, and back to write mode
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite))
+                    {
+                        s.WriteByte(5);
+                        Assert.AreEqual(3, s.ReadByte());
+                        s.WriteByte(13);
+
+                        Assert.AreEqual(3, s.Position);
+                    }
+
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Read))
+                    {
+                        Assert.AreEqual(7, s.Length);
+
+                        var buffer = new byte[s.Length];
+                        Assert.AreEqual(7, s.Read(buffer, offset: 0, buffer.Length));
+
+                        CollectionAssert.AreEqual(new byte[] { 5, 3, 13, 1, 6, 11, 12 }, buffer);
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, s.ReadByte());
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_SftpFileStream_SetLength_ReduceLength()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var s = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write))
+                    {
+                        s.Write(new byte[] { 5, 4, 3, 2, 1 }, 1, 3);
+                    }
+
+                    // reduce length while in write mode, with data in write buffer, and before
+                    // current position
+                    using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write))
+                    {
+                        s.Position = 3;
+                        s.Write(new byte[] { 6, 7, 8, 9 }, offset: 0, count: 4);
+
+                        Assert.AreEqual(7, s.Position);
+
+                        // verify buffer has not yet been flushed
+                        using (var fs = client.Open(remoteFile, FileMode.Open, FileAccess.Read))
+                        {
+                            Assert.AreEqual(4, fs.ReadByte());
+                            Assert.AreEqual(3, fs.ReadByte());
+                            Assert.AreEqual(2, fs.ReadByte());
+
+                            // Ensure we've reached end of the stream
+                            Assert.AreEqual(-1, fs.ReadByte());
+                        }
+
+                        s.SetLength(5);
+
+                        Assert.AreEqual(5, s.Position);
+
+                        // verify that buffer was flushed and size has been modified
+                        using (var fs = client.Open(remoteFile, FileMode.Open, FileAccess.Read))
+                        {
+                            Assert.AreEqual(4, fs.ReadByte());
+                            Assert.AreEqual(3, fs.ReadByte());
+                            Assert.AreEqual(2, fs.ReadByte());
+                            Assert.AreEqual(6, fs.ReadByte());
+                            Assert.AreEqual(7, fs.ReadByte());
+
+                            // Ensure we've reached end of the stream
+                            Assert.AreEqual(-1, fs.ReadByte());
+                        }
+
+                        s.WriteByte(1);
+                    }
+
+                    // verify that last byte was correctly written to the file
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Read))
+                    {
+                        Assert.AreEqual(6, s.Length);
+
+                        var buffer = new byte[s.Length + 2];
+                        Assert.AreEqual(6, s.Read(buffer, offset: 0, buffer.Length));
+
+                        CollectionAssert.AreEqual(new byte[] { 4, 3, 2, 6, 7, 1, 0, 0 }, buffer);
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, s.ReadByte());
+                    }
+
+                    // reduce length while in read mode, but beyond current position
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite))
+                    {
+                        var buffer = new byte[1];
+                        Assert.AreEqual(1, s.Read(buffer, offset: 0, buffer.Length));
+
+                        CollectionAssert.AreEqual(new byte[] { 4 }, buffer);
+
+                        s.SetLength(3);
+
+                        using (var w = client.Open(remoteFile, FileMode.Open, FileAccess.Write))
+                        {
+                            w.Write(new byte[] { 8, 1, 6, 2 }, offset: 0, count: 4);
+                        }
+
+                        // verify that position was not changed
+                        Assert.AreEqual(1, s.Position);
+
+                        // verify that read buffer was cleared
+                        Assert.AreEqual(1, s.ReadByte());
+                        Assert.AreEqual(6, s.ReadByte());
+                        Assert.AreEqual(2, s.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, s.ReadByte());
+
+                        Assert.AreEqual(4, s.Length);
+                    }
+
+                    // reduce length while in read mode, but before current position
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite))
+                    {
+                        var buffer = new byte[4];
+                        Assert.AreEqual(4, s.Read(buffer, offset: 0, buffer.Length));
+
+                        CollectionAssert.AreEqual(new byte[] { 8, 1, 6, 2 }, buffer);
+
+                        Assert.AreEqual(4, s.Position);
+
+                        s.SetLength(3);
+
+                        // verify that position was moved to last byte
+                        Assert.AreEqual(3, s.Position);
+
+                        using (var w = client.Open(remoteFile, FileMode.Open, FileAccess.Read))
+                        {
+                            Assert.AreEqual(3, w.Length);
+
+                            Assert.AreEqual(8, w.ReadByte());
+                            Assert.AreEqual(1, w.ReadByte());
+                            Assert.AreEqual(6, w.ReadByte());
+
+                            // Ensure we've reached end of the stream
+                            Assert.AreEqual(-1, w.ReadByte());
+                        }
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, s.ReadByte());
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginBegin()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.BufferSize = 500;
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF but not beyond buffer size
+                    // do not write anything
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: 3L, SeekOrigin.Begin);
+
+                        Assert.AreEqual(3, newPosition);
+                        Assert.AreEqual(3, fs.Position);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF and beyond buffer size
+                    // do not write anything
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: 700L, SeekOrigin.Begin);
+
+                        Assert.AreEqual(700, newPosition);
+                        Assert.AreEqual(700, fs.Position);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF but not beyond buffer size
+                    // write less bytes than buffer size
+                    var seekOffset = 3L;
+
+                    // buffer holding the data that we'll write to the file
+                    var writeBuffer = GenerateRandom(size: 7);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin);
+
+                        Assert.AreEqual(seekOffset, newPosition);
+                        Assert.AreEqual(seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBufferffer = new byte[seekOffset - 1];
+                        Assert.AreEqual(soughtOverReadBufferffer.Length, fs.Read(soughtOverReadBufferffer, offset: 0, soughtOverReadBufferffer.Length));
+                        Assert.IsTrue(new byte[soughtOverReadBufferffer.Length].IsEqualTo(soughtOverReadBufferffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF and beyond buffer size
+                    // write less bytes than buffer size
+                    seekOffset = 700L;
+
+                    // buffer holding the data that we'll write to the file
+                    writeBuffer = GenerateRandom(size: 4);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin);
+
+                        Assert.AreEqual(seekOffset, newPosition);
+                        Assert.AreEqual(seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBufferffer = new byte[seekOffset - 1];
+                        Assert.AreEqual(soughtOverReadBufferffer.Length, fs.Read(soughtOverReadBufferffer, offset: 0, soughtOverReadBufferffer.Length));
+                        Assert.IsTrue(new byte[soughtOverReadBufferffer.Length].IsEqualTo(soughtOverReadBufferffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF but not beyond buffer size
+                    // write more bytes than buffer size
+                    writeBuffer = GenerateRandom(size: 600);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: 3L, SeekOrigin.Begin);
+
+                        Assert.AreEqual(3, newPosition);
+                        Assert.AreEqual(3, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(3 + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+                        Assert.AreEqual(0x00, fs.ReadByte());
+                        Assert.AreEqual(0x00, fs.ReadByte());
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF and beyond buffer size
+                    // write more bytes than buffer size
+                    writeBuffer = GenerateRandom(size: 600);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: 550, SeekOrigin.Begin);
+
+                        Assert.AreEqual(550, newPosition);
+                        Assert.AreEqual(550, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(550 + writeBuffer.Length, fs.Length);
+
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBuffer = new byte[550 - 1];
+                        Assert.AreEqual(550 - 1, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length));
+                        Assert.IsTrue(new byte[550 - 1].IsEqualTo(soughtOverReadBuffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginEnd()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.BufferSize = 500;
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF but not beyond buffer size
+                    // do not write anything
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: 3L, SeekOrigin.End);
+
+                        Assert.AreEqual(4, newPosition);
+                        Assert.AreEqual(4, fs.Position);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF and beyond buffer size
+                    // do not write anything
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: 700L, SeekOrigin.End);
+
+                        Assert.AreEqual(701, newPosition);
+                        Assert.AreEqual(701, fs.Position);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF but not beyond buffer size
+                    // write less bytes than buffer size
+                    var seekOffset = 3L;
+
+                    // buffer holding the data that we'll write to the file
+                    var writeBuffer = GenerateRandom(size: 7);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.End);
+
+                        Assert.AreEqual(4, newPosition);
+                        Assert.AreEqual(4, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBuffer = new byte[seekOffset];
+                        Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length));
+                        Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF and beyond buffer size
+                    // write less bytes than buffer size
+                    seekOffset = 700L;
+
+                    // buffer holding the data that we'll write to the file
+                    writeBuffer = GenerateRandom(size: 4);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.End);
+
+                        Assert.AreEqual(1 + seekOffset, newPosition);
+                        Assert.AreEqual(1 + seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBuffer = new byte[seekOffset];
+                        Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length));
+                        Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF but not beyond buffer size
+                    // write more bytes than buffer size
+                    seekOffset = 3L;
+                    writeBuffer = GenerateRandom(size: 600);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.End);
+
+                        Assert.AreEqual(1 + seekOffset, newPosition);
+                        Assert.AreEqual(1 + seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBuffer = new byte[seekOffset];
+                        Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length));
+                        Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    // seek beyond EOF and beyond buffer size
+                    // write more bytes than buffer size
+                    seekOffset = 550L;
+                    writeBuffer = GenerateRandom(size: 600);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.End);
+
+                        Assert.AreEqual(1 + seekOffset, newPosition);
+                        Assert.AreEqual(1 + seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length);
+
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBuffer = new byte[seekOffset];
+                        Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length));
+                        Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_SftpFileStream_Seek_NegativeOffSet_SeekOriginEnd()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.BufferSize = 500;
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                        fs.WriteByte(0x07);
+                        fs.WriteByte(0x05);
+                    }
+
+                    // seek within file and not beyond buffer size
+                    // do not write anything
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: -2L, SeekOrigin.End);
+
+                        Assert.AreEqual(1, newPosition);
+                        Assert.AreEqual(1, fs.Position);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(3, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+                        Assert.AreEqual(0x07, fs.ReadByte());
+                        Assert.AreEqual(0x05, fs.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // buffer holding the data that we'll write to the file
+                    var writeBuffer = GenerateRandom(size: (int) client.BufferSize + 200);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    // seek within EOF and beyond buffer size
+                    // do not write anything
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: -100L, SeekOrigin.End);
+
+                        Assert.AreEqual(600, newPosition);
+                        Assert.AreEqual(600, fs.Position);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(writeBuffer.Length, fs.Length);
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // seek within EOF and within buffer size
+                    // write less bytes than buffer size
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+
+                        var newPosition = fs.Seek(offset: -3, SeekOrigin.End);
+
+                        Assert.AreEqual(697, newPosition);
+                        Assert.AreEqual(697, fs.Position);
+
+                        fs.WriteByte(0x01);
+                        fs.WriteByte(0x05);
+                        fs.WriteByte(0x04);
+                        fs.WriteByte(0x07);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(writeBuffer.Length + 1, fs.Length);
+
+                        var readBuffer = new byte[writeBuffer.Length - 3];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(readBuffer.SequenceEqual(writeBuffer.Take(readBuffer.Length)));
+
+                        Assert.AreEqual(0x01, fs.ReadByte());
+                        Assert.AreEqual(0x05, fs.ReadByte());
+                        Assert.AreEqual(0x04, fs.ReadByte());
+                        Assert.AreEqual(0x07, fs.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    // buffer holding the data that we'll write to the file
+                    writeBuffer = GenerateRandom(size: (int) client.BufferSize * 4);
+
+                    // seek within EOF and beyond buffer size
+                    // write less bytes than buffer size
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+
+                        var newPosition = fs.Seek(offset: -(client.BufferSize * 2), SeekOrigin.End);
+
+                        Assert.AreEqual(1000, newPosition);
+                        Assert.AreEqual(1000, fs.Position);
+
+                        fs.WriteByte(0x01);
+                        fs.WriteByte(0x05);
+                        fs.WriteByte(0x04);
+                        fs.WriteByte(0x07);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(writeBuffer.Length, fs.Length);
+
+                        // First part of file should not have been touched
+                        var readBuffer = new byte[(int) client.BufferSize * 2];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(readBuffer.SequenceEqual(writeBuffer.Take(readBuffer.Length)));
+
+                        // Check part that should have been updated
+                        Assert.AreEqual(0x01, fs.ReadByte());
+                        Assert.AreEqual(0x05, fs.ReadByte());
+                        Assert.AreEqual(0x04, fs.ReadByte());
+                        Assert.AreEqual(0x07, fs.ReadByte());
+
+                        // Remaining bytes should not have been touched
+                        readBuffer = new byte[((int) client.BufferSize * 2) - 4];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(readBuffer.SequenceEqual(writeBuffer.Skip(((int)client.BufferSize * 2) + 4).Take(readBuffer.Length)));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        /// https://github.com/sshnet/SSH.NET/issues/253
+        [TestMethod]
+        public void Sftp_SftpFileStream_Seek_Issue253()
+        {
+            var buf = Encoding.UTF8.GetBytes("123456");
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var ws = client.OpenWrite(remoteFile))
+                    {
+                        ws.Write(buf, offset: 0, count: 3);
+                    }
+
+                    using (var ws = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = ws.Seek(offset: 3, SeekOrigin.Begin);
+
+                        Assert.AreEqual(3, newPosition);
+                        Assert.AreEqual(3, ws.Position);
+
+                        ws.Write(buf, 3, 3);
+                    }
+
+                    var actual = client.ReadAllText(remoteFile, Encoding.UTF8);
+                    Assert.AreEqual("123456", actual);
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_SftpFileStream_Seek_WithinReadBuffer()
+        {
+            var originalContent = GenerateRandom(size: 800);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.BufferSize = 500;
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.Write(originalContent, offset: 0, originalContent.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        var readBuffer = new byte[200];
+
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+
+                        var newPosition = fs.Seek(offset: 3L, SeekOrigin.Begin);
+
+                        Assert.AreEqual(3L, newPosition);
+                        Assert.AreEqual(3L, fs.Position);
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    #region Seek beyond EOF and beyond buffer size do not write anything
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(offset: 700L, SeekOrigin.Begin);
+
+                        Assert.AreEqual(700L, newPosition);
+                        Assert.AreEqual(700L, fs.Position);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(1, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    #endregion Seek beyond EOF and beyond buffer size do not write anything
+
+                    #region Seek beyond EOF but not beyond buffer size and write less bytes than buffer size
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    var seekOffset = 3L;
+                    var writeBuffer = GenerateRandom(size: 7);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin);
+
+                        Assert.AreEqual(seekOffset, newPosition);
+                        Assert.AreEqual(seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBuffer = new byte[seekOffset - 1];
+                        Assert.AreEqual(soughtOverReadBuffer.Length, fs.Read(soughtOverReadBuffer, offset: 0, soughtOverReadBuffer.Length));
+                        Assert.IsTrue(new byte[soughtOverReadBuffer.Length].IsEqualTo(soughtOverReadBuffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    #endregion Seek beyond EOF but not beyond buffer size and write less bytes than buffer size
+
+                    #region Seek beyond EOF and beyond buffer size and write less bytes than buffer size
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    seekOffset = 700L;
+                    writeBuffer = GenerateRandom(size: 4);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin);
+
+                        Assert.AreEqual(seekOffset, newPosition);
+                        Assert.AreEqual(seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBufferffer = new byte[seekOffset - 1];
+                        Assert.AreEqual(soughtOverReadBufferffer.Length, fs.Read(soughtOverReadBufferffer, offset: 0, soughtOverReadBufferffer.Length));
+                        Assert.IsTrue(new byte[soughtOverReadBufferffer.Length].IsEqualTo(soughtOverReadBufferffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    #endregion Seek beyond EOF and beyond buffer size and write less bytes than buffer size
+
+                    #region Seek beyond EOF but not beyond buffer size and write more bytes than buffer size
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    seekOffset = 3L;
+                    writeBuffer = GenerateRandom(size: 600);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin);
+
+                        Assert.AreEqual(seekOffset, newPosition);
+                        Assert.AreEqual(seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length);
+                        Assert.AreEqual(0x04, fs.ReadByte());
+                        Assert.AreEqual(0x00, fs.ReadByte());
+                        Assert.AreEqual(0x00, fs.ReadByte());
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    #endregion Seek beyond EOF but not beyond buffer size and write more bytes than buffer size
+
+                    #region Seek beyond EOF and beyond buffer size and write more bytes than buffer size
+
+                    // create single-byte file
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        fs.WriteByte(0x04);
+                    }
+
+                    seekOffset = 550L;
+                    writeBuffer = GenerateRandom(size: 600);
+
+                    using (var fs = client.OpenWrite(remoteFile))
+                    {
+                        var newPosition = fs.Seek(seekOffset, SeekOrigin.Begin);
+
+                        Assert.AreEqual(seekOffset, newPosition);
+                        Assert.AreEqual(seekOffset, fs.Position);
+
+                        fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+                    }
+
+                    using (var fs = client.OpenRead(remoteFile))
+                    {
+                        Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length);
+
+                        Assert.AreEqual(0x04, fs.ReadByte());
+
+                        var soughtOverReadBufferffer = new byte[seekOffset - 1];
+                        Assert.AreEqual(seekOffset - 1, fs.Read(soughtOverReadBufferffer, offset: 0, soughtOverReadBufferffer.Length));
+                        Assert.IsTrue(new byte[seekOffset - 1].IsEqualTo(soughtOverReadBufferffer));
+
+                        var readBuffer = new byte[writeBuffer.Length];
+                        Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
+                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+
+                        // Ensure we've reached end of the stream
+                        Assert.AreEqual(-1, fs.ReadByte());
+                    }
+
+                    client.DeleteFile(remoteFile);
+
+                    #endregion Seek beyond EOF and beyond buffer size and write more bytes than buffer size
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_SftpFileStream_SetLength_FileDoesNotExist()
+        {
+            var size = new Random().Next(500, 5000);
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write))
+                    {
+                        s.SetLength(size);
+                    }
+
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var attributes = client.GetAttributes(remoteFile);
+                    Assert.IsTrue(attributes.IsRegularFile);
+                    Assert.AreEqual(size, attributes.Size);
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(new byte[size]), CreateHash(downloaded));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_Append_Write_ExistingFile()
+        {
+            const int fileSize = 5 * 1024;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            using (var input = CreateMemoryStream(fileSize))
+            {
+                input.Position = 0;
+
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.UploadFile(input, remoteFile);
+
+                    using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write))
+                    {
+                        var buffer = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 };
+                        s.Write(buffer, offset: 0, buffer.Length);
+                        input.Write(buffer, offset: 0, buffer.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+
+                        input.Position = 0;
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(input), CreateHash(downloaded));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_Append_Write_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    #region Verify if merely opening the file for append creates a zero-byte file
+
+                    using (client.Open(remoteFile, FileMode.Append, FileAccess.Write))
+                    {
+                    }
+
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var attributes = client.GetAttributes(remoteFile);
+                    Assert.IsTrue(attributes.IsRegularFile);
+                    Assert.AreEqual(0L, attributes.Size);
+
+                    #endregion Verify if merely opening the file for append creates it
+
+                    client.DeleteFile(remoteFile);
+
+                    #region Verify if content is actually written to the file
+
+                    var content = GenerateRandom(size: 100);
+
+                    using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write))
+                    {
+                        s.Write(content, offset: 0, content.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
+                    }
+
+                    #endregion Verify if content is actually written to the file
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+
+
+        [TestMethod]
+        public void Sftp_Open_PathAndMode_ModeIsCreate_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    #region Verify if merely opening the file for create creates a zero-byte file
+
+                    using (client.Open(remoteFile, FileMode.Create))
+                    {
+                    }
+
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var attributes = client.GetAttributes(remoteFile);
+                    Assert.IsTrue(attributes.IsRegularFile);
+                    Assert.AreEqual(0L, attributes.Size);
+
+                    #endregion Verify if merely opening the file for create creates a zero-byte file
+
+                    client.DeleteFile(remoteFile);
+
+                    #region Verify if content is actually written to the file
+
+                    var content = GenerateRandom(size: 100);
+
+                    using (var s = client.Open(remoteFile, FileMode.Create))
+                    {
+                        s.Write(content, offset: 0, content.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
+                    }
+
+                    #endregion Verify if content is actually written to the file
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_PathAndMode_ModeIsCreate_ExistingFile()
+        {
+            const int fileSize = 5 * 1024;
+            var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            using (var input = CreateMemoryStream(fileSize))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    input.Position = 0;
+                    client.UploadFile(input, remoteFile);
+
+                    using (var stream = client.Open(remoteFile, FileMode.Create))
+                    {
+                        // Verify if merely opening the file for create overwrites the file
+                        var attributes = client.GetAttributes(remoteFile);
+                        Assert.IsTrue(attributes.IsRegularFile);
+                        Assert.AreEqual(0L, attributes.Size);
+
+                        stream.Write(newContent, offset: 0, newContent.Length);
+                        stream.Position = 0;
+
+                        Assert.AreEqual(CreateHash(newContent), CreateHash(stream));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsReadWrite_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    #region Verify if merely opening the file for create creates a zero-byte file
+
+                    using (client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite))
+                    {
+                    }
+
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var attributes = client.GetAttributes(remoteFile);
+                    Assert.IsTrue(attributes.IsRegularFile);
+                    Assert.AreEqual(0L, attributes.Size);
+
+                    #endregion Verify if merely opening the file for create creates a zero-byte file
+
+                    client.DeleteFile(remoteFile);
+
+                    #region Verify if content is actually written to the file
+
+                    var content = GenerateRandom(size: 100);
+
+                    using (var s = client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite))
+                    {
+                        s.Write(content, offset: 0, content.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
+                    }
+
+                    #endregion Verify if content is actually written to the file
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsReadWrite_ExistingFile()
+        {
+            const int fileSize = 5 * 1024;
+            var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            using (var input = CreateMemoryStream(fileSize))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    input.Position = 0;
+                    client.UploadFile(input, remoteFile);
+
+                    using (var stream = client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite))
+                    {
+                        // Verify if merely opening the file for create overwrites the file
+                        var attributes = client.GetAttributes(remoteFile);
+                        Assert.IsTrue(attributes.IsRegularFile);
+                        Assert.AreEqual(0L, attributes.Size);
+
+                        stream.Write(newContent, offset: 0, newContent.Length);
+                        stream.Position = 0;
+
+                        Assert.AreEqual(CreateHash(newContent), CreateHash(stream));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsWrite_ExistingFile()
+        {
+            // use new content that contains less bytes than original content to
+            // verify whether file is first truncated
+            var originalContent = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 };
+            var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllBytes(remoteFile, originalContent);
+
+                    using (var s = client.Open(remoteFile, FileMode.Create, FileAccess.Write))
+                    {
+                        s.Write(newContent, offset: 0, newContent.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(newContent), CreateHash(downloaded));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsWrite_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    #region Verify if merely opening the file for create creates a zero-byte file
+
+                    using (client.Open(remoteFile, FileMode.Create, FileAccess.Write))
+                    {
+                    }
+
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var attributes = client.GetAttributes(remoteFile);
+                    Assert.IsTrue(attributes.IsRegularFile);
+                    Assert.AreEqual(0L, attributes.Size);
+
+                    #endregion Verify if merely opening the file for create creates a zero-byte file
+
+                    client.DeleteFile(remoteFile);
+
+                    #region Verify if content is actually written to the file
+
+                    var content = GenerateRandom(size: 100);
+
+                    using (var s = client.Open(remoteFile, FileMode.Create, FileAccess.Write))
+                    {
+                        s.Write(content, offset: 0, content.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
+                    }
+
+                    #endregion Verify if content is actually written to the file
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_CreateNew_Write_ExistingFile()
+        {
+            const int fileSize = 5 * 1024;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            using (var input = CreateMemoryStream(fileSize))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                input.Position = 0;
+
+                try
+                {
+                    client.UploadFile(input, remoteFile);
+
+                    Stream stream = null;
+
+                    try
+                    {
+                        stream = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write);
+                        Assert.Fail();
+                    }
+                    catch (SshException)
+                    {
+                    }
+                    finally
+                    {
+                        stream?.Dispose();
+                    }
+
+                    // Verify that the file was not modified
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+
+                        input.Position = 0;
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(input), CreateHash(downloaded));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_CreateNew_Write_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    #region Verify if merely opening the file creates a zero-byte file
+
+                    using (client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write))
+                    {
+                    }
+
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var attributes = client.GetAttributes(remoteFile);
+                    Assert.IsTrue(attributes.IsRegularFile);
+                    Assert.AreEqual(0L, attributes.Size);
+
+                    #endregion Verify if merely opening the file creates it
+
+                    client.DeleteFile(remoteFile);
+
+                    #region Verify if content is actually written to the file
+
+                    var content = GenerateRandom(size: 100);
+
+                    using (var s = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write))
+                    {
+                        s.Write(content, offset: 0, content.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
+                    }
+
+                    #endregion Verify if content is actually written to the file
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_Open_Write_ExistingFile()
+        {
+            // use new content that contains less bytes than original content to
+            // verify whether file is first truncated
+            var originalContent = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 };
+            var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b };
+            var expectedContent = new byte[] { 0x07, 0x03, 0x02, 0x0b, 0x04 };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllBytes(remoteFile, originalContent);
+
+                    using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Write))
+                    {
+                        s.Write(newContent, offset: 0, newContent.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(expectedContent), CreateHash(downloaded));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_Open_Write_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    Stream stream = null;
+
+                    try
+                    {
+                        stream = client.Open(remoteFile, FileMode.Open, FileAccess.Write);
+                        Assert.Fail();
+                    }
+                    catch (SshException)
+                    {
+                    }
+                    finally
+                    {
+                        stream?.Dispose();
+                    }
+
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_OpenOrCreate_Write_ExistingFile()
+        {
+            // use new content that contains less bytes than original content to
+            // verify whether file is first truncated
+            var originalContent = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 };
+            var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b };
+            var expectedContent = new byte[] { 0x07, 0x03, 0x02, 0x0b, 0x04 };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    client.WriteAllBytes(remoteFile, originalContent);
+
+                    using (var s = client.Open(remoteFile, FileMode.OpenOrCreate, FileAccess.Write))
+                    {
+                        s.Write(newContent, offset: 0, newContent.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(expectedContent), CreateHash(downloaded));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_OpenOrCreate_Write_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    #region Verify if merely opening the file creates a zero-byte file
+
+                    using (client.Open(remoteFile, FileMode.OpenOrCreate, FileAccess.Write))
+                    {
+                    }
+
+                    Assert.IsTrue(client.Exists(remoteFile));
+
+                    var attributes = client.GetAttributes(remoteFile);
+                    Assert.IsTrue(attributes.IsRegularFile);
+                    Assert.AreEqual(0L, attributes.Size);
+
+                    #endregion Verify if merely opening the file creates it
+
+                    client.DeleteFile(remoteFile);
+
+                    #region Verify if content is actually written to the file
+
+                    var content = GenerateRandom(size: 100);
+
+                    using (var s = client.Open(remoteFile, FileMode.OpenOrCreate, FileAccess.Write))
+                    {
+                        s.Write(content, offset: 0, content.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
+                    }
+
+                    #endregion Verify if content is actually written to the file
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+
+
+
+
+
+
+        [TestMethod]
+        public void Sftp_Open_Truncate_Write_ExistingFile()
+        {
+            const int fileSize = 5 * 1024;
+
+            // use new content that contains less bytes than original content to
+            // verify whether file is first truncated
+            var originalContent = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 };
+            var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b };
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            using (var input = CreateMemoryStream(fileSize))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                input.Position = 0;
+
+                try
+                {
+                    client.WriteAllBytes(remoteFile, originalContent);
+
+                    using (var s = client.Open(remoteFile, FileMode.Truncate, FileAccess.Write))
+                    {
+                        s.Write(newContent, offset: 0, newContent.Length);
+                    }
+
+                    using (var downloaded = new MemoryStream())
+                    {
+                        client.DownloadFile(remoteFile, downloaded);
+
+                        input.Position = 0;
+                        downloaded.Position = 0;
+                        Assert.AreEqual(CreateHash(newContent), CreateHash(downloaded));
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_Open_Truncate_Write_FileDoesNotExist()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                try
+                {
+                    Stream stream = null;
+
+                    try
+                    {
+                        stream = client.Open(remoteFile, FileMode.Truncate, FileAccess.Write);
+                        Assert.Fail();
+                    }
+                    catch (SshException)
+                    {
+                    }
+
+                    Assert.IsFalse(client.Exists(remoteFile));
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Sftp_OpenRead()
+        {
+            const int fileSize = 5 * 1024 * 1024;
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var remoteFile = GenerateUniqueRemoteFileName();
+
+                SftpCreateRemoteFile(client, remoteFile, fileSize);
+
+                try
+                {
+                    using (var s = client.OpenRead(remoteFile))
+                    {
+                        var buffer = new byte[s.Length];
+
+                        var stopwatch = new Stopwatch();
+                        stopwatch.Start();
+
+                        var bytesRead = s.Read(buffer, offset: 0, buffer.Length);
+
+                        stopwatch.Stop();
+
+                        var transferSpeed = CalculateTransferSpeed(bytesRead, stopwatch.ElapsedMilliseconds);
+                        Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
+                        Console.WriteLine(@"Transfer speed: {0:N2} KB/s", transferSpeed);
+
+                        Assert.AreEqual(fileSize, bytesRead);
+                    }
+                }
+                finally
+                {
+                    if (client.Exists(remoteFile))
+                    {
+                        client.DeleteFile(remoteFile);
+                    }
+                }
+            }
+        }
+
+        private static IEnumerable<object[]> GetSftpUploadFileFileStreamData()
+        {
+            yield return new object[] { 0 };
+            yield return new object[] { 5 * 1024 * 1024 };
+        }
+
+        private static Encoding GetRandomEncoding()
+        {
+            var random = new Random().Next(1, 3);
+            switch (random)
+            {
+                case 1:
+                    return Encoding.Unicode;
+                case 2:
+                    return Encoding.UTF8;
+                case 3:
+                    return Encoding.UTF32;
+                default:
+                    throw new NotImplementedException();
+            }
+        }
+
+        private static byte[] GetBytesWithPreamble(string text, Encoding encoding)
+        {
+            var preamble = encoding.GetPreamble();
+            var textBytes = encoding.GetBytes(text);
+
+            if (preamble.Length != 0)
+            {
+                var textAndPreambleBytes = new byte[preamble.Length + textBytes.Length];
+                Buffer.BlockCopy(preamble, srcOffset: 0, textAndPreambleBytes, dstOffset: 0, preamble.Length);
+                Buffer.BlockCopy(textBytes, srcOffset: 0, textAndPreambleBytes, preamble.Length, textBytes.Length);
+                return textAndPreambleBytes;
+            }
+
+            return textBytes;
+        }
+
+        private static Stream GetResourceStream(string resourceName)
+        {
+            var type = typeof(SftpTests);
+            var resourceStream = type.Assembly.GetManifestResourceStream(resourceName);
+            if (resourceStream == null)
+            {
+                throw new ArgumentException($"Resource '{resourceName}' not found in assembly '{type.Assembly.FullName}'.", nameof(resourceName));
+            }
+            return resourceStream;
+        }
+
+        private static decimal CalculateTransferSpeed(long length, long elapsedMilliseconds)
+        {
+            return (length / 1024m) / (elapsedMilliseconds / 1000m);
+        }
+
+        private static void SftpCreateRemoteFile(SftpClient client, string remoteFile, int size)
+        {
+            var file = CreateTempFile(size);
+
+            try
+            {
+                using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
+                {
+                    client.UploadFile(fs, remoteFile);
+                }
+            }
+            finally
+            {
+                File.Delete(file);
+            }
+        }
+
+        private static byte[] GenerateRandom(int size)
+        {
+            var random = new Random();
+            var randomContent = new byte[size];
+            random.NextBytes(randomContent);
+            return randomContent;
+        }
+
+        private static Stream CreateStreamWithContent(string content)
+        {
+            var memoryStream = new MemoryStream();
+            var sw = new StreamWriter(memoryStream, Encoding.ASCII, 1024);
+            sw.Write(content);
+            sw.Flush();
+            memoryStream.Position = 0;
+            return memoryStream;
+        }
+
+        private static string GenerateUniqueRemoteFileName()
+        {
+            return $"/home/sshnet/{Guid.NewGuid():D}";
+        }
+    }
+}

+ 1 - 3
src/Renci.SshNet.IntegrationTests/SshClientTests.cs

@@ -1,6 +1,4 @@
-using Renci.SshNet;
-
-namespace IntegrationTests
+namespace Renci.SshNet.IntegrationTests
 {
     /// <summary>
     /// The SSH client integration tests

+ 41 - 0
src/Renci.SshNet.IntegrationTests/SshConnectionDisruptor.cs

@@ -0,0 +1,41 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    internal class SshConnectionDisruptor
+    {
+        private readonly IConnectionInfoFactory _connectionInfoFactory;
+
+        public SshConnectionDisruptor(IConnectionInfoFactory connectionInfoFactory)
+        {
+            _connectionInfoFactory = connectionInfoFactory;
+        }
+
+        public SshConnectionRestorer BreakConnections()
+        {
+            var client = new SshClient(_connectionInfoFactory.Create());
+            
+            client.Connect();
+
+            PauseSshd(client);
+            
+            return new SshConnectionRestorer(client);
+        }
+
+        private static void PauseSshd(SshClient client)
+        {
+            var command = client.CreateCommand("sudo echo 'DenyUsers sshnet' >> /etc/ssh/sshd_config");
+            var output = command.Execute();
+            if (command.ExitStatus != 0)
+            {
+                throw new ApplicationException(
+                    $"Blocking user sshnet failed with exit code {command.ExitStatus}.\r\n{output}\r\n{command.Error}");
+            }
+            command = client.CreateCommand("sudo pkill -9 -U sshnet -f sshd.pam");
+            output = command.Execute();
+            if (command.ExitStatus != 0)
+            {
+                throw new ApplicationException(
+                    $"Killing sshd.pam service failed with exit code {command.ExitStatus}.\r\n{output}\r\n{command.Error}");
+            }
+        }
+    }
+}

+ 36 - 0
src/Renci.SshNet.IntegrationTests/SshConnectionRestorer.cs

@@ -0,0 +1,36 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    internal class SshConnectionRestorer : IDisposable
+    {
+        private SshClient _sshClient;
+
+        public SshConnectionRestorer(SshClient sshClient)
+        {
+            _sshClient = sshClient;
+        }
+
+        public void RestoreConnections()
+        {
+            var command = _sshClient.CreateCommand("sudo sed -i '/DenyUsers sshnet/d' /etc/ssh/sshd_config");
+            var output = command.Execute();
+            if (command.ExitStatus != 0)
+            {
+                throw new ApplicationException(
+                    $"Unblocking user sshnet failed with exit code {command.ExitStatus}.\r\n{output}\r\n{command.Error}");
+            }
+            command = _sshClient.CreateCommand("sudo /usr/sbin/sshd.pam");
+            output = command.Execute();
+            if (command.ExitStatus != 0)
+            {
+                throw new ApplicationException(
+                    $"Resuming ssh service failed with exit code {command.ExitStatus}.\r\n{output}\r\n{command.Error}");
+            }
+        }
+
+        public void Dispose()
+        {
+            _sshClient?.Dispose();
+            _sshClient = null;
+        }
+    }
+}

+ 972 - 0
src/Renci.SshNet.IntegrationTests/SshTests.cs

@@ -0,0 +1,972 @@
+using System.ComponentModel;
+using System.Net;
+using System.Net.Sockets;
+
+using Renci.SshNet.Common;
+using Renci.SshNet.IntegrationTests.Common;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    [TestClass]
+    public class SshTests : TestBase
+    {
+        private IConnectionInfoFactory _connectionInfoFactory;
+        private IConnectionInfoFactory _adminConnectionInfoFactory;
+        private RemoteSshdConfig _remoteSshdConfig;
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort);
+            _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(SshServerHostName, SshServerPort);
+
+            _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig();
+            _remoteSshdConfig.AllowTcpForwarding()
+                             .PrintMotd(false)
+                             .Update()
+                             .Restart();
+        }
+
+        [TestCleanup]
+        public void TearDown()
+        {
+            _remoteSshdConfig?.Reset();
+        }
+
+        /// <summary>
+        /// Test for a channel that is being closed by the server.
+        /// </summary>
+        [TestMethod]
+        public void Ssh_ShellStream_Exit()
+        {
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                var terminalModes = new Dictionary<TerminalModes, uint>
+                    {
+                        { TerminalModes.ECHO, 0 }
+                    };
+
+                using (var shellStream = client.CreateShellStream("xterm", 80, 24, 800, 600, 1024, terminalModes))
+                {
+                    shellStream.WriteLine("echo Hello!");
+                    shellStream.WriteLine("exit");
+
+                    Thread.Sleep(1000);
+
+                    try
+                    {
+                        shellStream.Write("ABC");
+                        Assert.Fail();
+                    }
+                    catch (ObjectDisposedException ex)
+                    {
+                        Assert.IsNull(ex.InnerException);
+                        Assert.AreEqual("ShellStream", ex.ObjectName);
+                        Assert.AreEqual($"Cannot access a disposed object.{Environment.NewLine}Object name: '{ex.ObjectName}'.", ex.Message);
+                    }
+
+                    var line = shellStream.ReadLine();
+                    Assert.IsNotNull(line);
+                    Assert.IsTrue(line.EndsWith("Hello!"), line);
+
+                    // TODO: ReadLine should return null when the buffer is empty and the channel has been closed (issue #672)
+                    try
+                    {
+                        line = shellStream.ReadLine();
+                        Assert.Fail(line);
+                    }
+                    catch (NullReferenceException)
+                    {
+
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// https://github.com/sshnet/SSH.NET/issues/63
+        /// </summary>
+        [TestMethod]
+        [Category("Reproduction Tests")]
+        [Ignore]
+        public void Ssh_ShellStream_IntermittendOutput()
+        {
+            const string remoteFile = "/home/sshnet/test.sh";
+
+            var expectedResult = string.Join("\n",
+                                             "Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 5 ",
+                                             "Line 6");
+
+            var scriptBuilder = new StringBuilder();
+            scriptBuilder.Append("#!/bin/sh\n");
+            scriptBuilder.Append("echo Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("sleep .5\n");
+            scriptBuilder.Append("echo Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("echo Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("echo Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("sleep 2\n");
+            scriptBuilder.Append("echo \"Line 5 \"\n");
+            scriptBuilder.Append("echo Line 6 \n");
+            scriptBuilder.Append("exit 13\n");
+
+            using (var sshClient = new SshClient(_connectionInfoFactory.Create()))
+            {
+                sshClient.Connect();
+
+                CreateShellScript(_connectionInfoFactory, remoteFile, scriptBuilder.ToString());
+
+                try
+                {
+                    var terminalModes = new Dictionary<TerminalModes, uint>
+                    {
+                        { TerminalModes.ECHO, 0 }
+                    };
+
+                    using (var shellStream = sshClient.CreateShellStream("xterm", 80, 24, 800, 600, 1024, terminalModes))
+                    {
+                        shellStream.WriteLine(remoteFile);
+                        Thread.Sleep(1200);
+                        using (var reader = new StreamReader(shellStream, new UTF8Encoding(false), false, 10))
+                        {
+                            var lines = new List<string>();
+                            string line = null;
+                            while ((line = reader.ReadLine()) != null)
+                            {
+                                lines.Add(line);
+                            }
+                            Assert.AreEqual(6, lines.Count, string.Join("\n", lines));
+                            Assert.AreEqual(expectedResult, string.Join("\n", lines));
+                        }
+                    }
+                }
+                finally
+                {
+                    RemoveFileOrDirectory(sshClient, remoteFile);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Issue 1555
+        /// </summary>
+        [TestMethod]
+        public void Ssh_CreateShell()
+        {
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                using (var input = new MemoryStream())
+                using (var output = new MemoryStream())
+                using (var extOutput = new MemoryStream())
+                {
+                    var shell = client.CreateShell(input, output, extOutput);
+                    shell.Start();
+
+                    var inputWriter = new StreamWriter(input, Encoding.ASCII, 1024);
+                    inputWriter.WriteLine("echo $PATH");
+
+                    var outputReader = new StreamReader(output, Encoding.ASCII, false, 1024);
+                    Console.WriteLine(outputReader.ReadToEnd());
+
+                    shell.Stop();
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Ssh_Command_IntermittendOutput_EndExecute()
+        {
+            const string remoteFile = "/home/sshnet/test.sh";
+
+            var expectedResult = string.Join("\n",
+                                             "Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 5 ",
+                                             "Line 6",
+                                             "");
+
+            var scriptBuilder = new StringBuilder();
+            scriptBuilder.Append("#!/bin/sh\n");
+            scriptBuilder.Append("echo Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("sleep .5\n");
+            scriptBuilder.Append("echo Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("echo Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("echo Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("sleep 2\n");
+            scriptBuilder.Append("echo \"Line 5 \"\n");
+            scriptBuilder.Append("echo Line 6 \n");
+            scriptBuilder.Append("exit 13\n");
+
+            using (var sshClient = new SshClient(_connectionInfoFactory.Create()))
+            {
+                sshClient.Connect();
+
+                CreateShellScript(_connectionInfoFactory, remoteFile, scriptBuilder.ToString());
+
+                try
+                {
+                    using (var cmd = sshClient.CreateCommand("chmod 777 " + remoteFile))
+                    {
+                        cmd.Execute();
+
+                        Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
+                    }
+
+                    using (var command = sshClient.CreateCommand(remoteFile))
+                    {
+                        var asyncResult = command.BeginExecute();
+                        var actualResult = command.EndExecute(asyncResult);
+
+                        Assert.AreEqual(expectedResult, actualResult);
+                        Assert.AreEqual(expectedResult, command.Result);
+                        Assert.AreEqual(13, command.ExitStatus);
+                    }
+                }
+                finally
+                {
+                    RemoveFileOrDirectory(sshClient, remoteFile);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Ignored for now, because:
+        /// * OutputStream.Read(...) does not block when no data is available
+        /// * SshCommand.(Begin)Execute consumes *OutputStream*, advancing its position.
+        /// 
+        /// https://github.com/sshnet/SSH.NET/issues/650
+        /// </summary>
+        [TestMethod]
+        [Ignore]
+        public void Ssh_Command_IntermittendOutput_OutputStream()
+        {
+            const string remoteFile = "/home/sshnet/test.sh";
+
+            var expectedResult = string.Join("\n",
+                                             "Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                             "Line 5 ",
+                                             "Line 6");
+
+            var scriptBuilder = new StringBuilder();
+            scriptBuilder.Append("#!/bin/sh\n");
+            scriptBuilder.Append("echo Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("sleep .5\n");
+            scriptBuilder.Append("echo Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("echo Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("echo Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
+            scriptBuilder.Append("sleep 2\n");
+            scriptBuilder.Append("echo \"Line 5 \"\n");
+            scriptBuilder.Append("echo Line 6 \n");
+            scriptBuilder.Append("exit 13\n");
+
+            using (var sshClient = new SshClient(_connectionInfoFactory.Create()))
+            {
+                sshClient.Connect();
+
+                CreateShellScript(_connectionInfoFactory, remoteFile, scriptBuilder.ToString());
+
+                try
+                {
+                    using (var cmd = sshClient.CreateCommand("chmod 777 " + remoteFile))
+                    {
+                        cmd.Execute();
+
+                        Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
+                    }
+
+                    using (var command = sshClient.CreateCommand(remoteFile))
+                    {
+                        var asyncResult = command.BeginExecute();
+
+                        using (var reader = new StreamReader(command.OutputStream, new UTF8Encoding(false), false, 10))
+                        {
+                            var lines = new List<string>();
+                            string line = null;
+                            while ((line = reader.ReadLine()) != null)
+                            {
+                                lines.Add(line);
+                            }
+
+                            Assert.AreEqual(6, lines.Count, string.Join("\n", lines));
+                            Assert.AreEqual(expectedResult, string.Join("\n", lines));
+                            Assert.AreEqual(13, command.ExitStatus);
+                        }
+
+                        var actualResult = command.EndExecute(asyncResult);
+
+                        Assert.AreEqual(expectedResult, actualResult);
+                        Assert.AreEqual(expectedResult, command.Result);
+                    }
+                }
+                finally
+                {
+                    RemoveFileOrDirectory(sshClient, remoteFile);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Ssh_DynamicPortForwarding_DisposeSshClientWithoutStoppingPort()
+        {
+            const string searchText = "HTTP/1.1 301 Moved Permanently";
+            const string hostName = "github.com";
+
+            var httpGetRequest = Encoding.ASCII.GetBytes($"GET / HTTP/1.1\r\nHost: {hostName}\r\n\r\n");
+            Socket socksSocket;
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(200);
+                client.Connect();
+
+                var forwardedPort = new ForwardedPortDynamic(1080);
+                forwardedPort.Exception += (sender, args) => Console.WriteLine(args.Exception.ToString());
+                client.AddForwardedPort(forwardedPort);
+                forwardedPort.Start();
+
+                var socksClient = new Socks5Handler(new IPEndPoint(IPAddress.Loopback, 1080),
+                                                    string.Empty,
+                                                    string.Empty);
+
+                socksSocket = socksClient.Connect(hostName, 80);
+                socksSocket.Send(httpGetRequest);
+
+                var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII);
+                Assert.IsTrue(httpResponse.Contains(searchText), httpResponse);
+            }
+
+            Assert.IsTrue(socksSocket.Connected);
+
+            // check if client socket was properly closed
+            Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None));
+        }
+
+        [TestMethod]
+        public void Ssh_DynamicPortForwarding_DomainName()
+        {
+            const string searchText = "HTTP/1.1 301 Moved Permanently";
+            const string hostName = "github.com";
+
+            // Set-up a host alias for google.be on the remote server that is not known locally; this allows us to
+            // verify whether the host name is resolved remotely.
+            const string hostNameAlias = "dynamicportforwarding-test.for.sshnet";
+
+            // Construct a HTTP request for which we expected the response to contain the search text.
+            var httpGetRequest = Encoding.ASCII.GetBytes($"GET / HTTP/1.1\r\nHost: {hostName}\r\n\r\n");
+
+            var ipAddresses = Dns.GetHostAddresses(hostName);
+            var hostsFileUpdated = AddOrUpdateHostsEntry(_adminConnectionInfoFactory, ipAddresses[0], hostNameAlias);
+
+            try
+            {
+                using (var client = new SshClient(_connectionInfoFactory.Create()))
+                {
+                    client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(200);
+                    client.Connect();
+
+                    var forwardedPort = new ForwardedPortDynamic(1080);
+                    forwardedPort.Exception += (sender, args) => Console.WriteLine(args.Exception.ToString());
+                    client.AddForwardedPort(forwardedPort);
+                    forwardedPort.Start();
+
+                    var socksClient = new Socks5Handler(new IPEndPoint(IPAddress.Loopback, 1080),
+                                                        string.Empty,
+                                                        string.Empty);
+                    var socksSocket = socksClient.Connect(hostNameAlias, 80);
+
+                    socksSocket.Send(httpGetRequest);
+                    var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII);
+                    Assert.IsTrue(httpResponse.Contains(searchText), httpResponse);
+
+                    // Verify if port is still open
+                    socksSocket.Send(httpGetRequest);
+                    GetHttpResponse(socksSocket, Encoding.ASCII);
+
+                    forwardedPort.Stop();
+
+                    Assert.IsTrue(socksSocket.Connected);
+
+                    // check if client socket was properly closed
+                    Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None));
+
+                    forwardedPort.Start();
+
+                    // create new SOCKS connection and very whether the forwarded port is functional again
+                    socksSocket = socksClient.Connect(hostNameAlias, 80);
+
+                    socksSocket.Send(httpGetRequest);
+                    httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII);
+                    Assert.IsTrue(httpResponse.Contains(searchText), httpResponse);
+
+                    forwardedPort.Dispose();
+
+                    Assert.IsTrue(socksSocket.Connected);
+
+                    // check if client socket was properly closed
+                    Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None));
+
+                    forwardedPort.Dispose();
+                }
+            }
+            finally
+            {
+                if (hostsFileUpdated)
+                {
+                    RemoveHostsEntry(_adminConnectionInfoFactory, ipAddresses[0], hostNameAlias);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Ssh_DynamicPortForwarding_IPv4()
+        {
+            const string searchText = "HTTP/1.1 301 Moved Permanently";
+            const string hostName = "github.com";
+
+            var httpGetRequest = Encoding.ASCII.GetBytes($"GET /null HTTP/1.1\r\nHost: {hostName}\r\n\r\n");
+
+            var ipv4 = Dns.GetHostAddresses(hostName).FirstOrDefault(p => p.AddressFamily == AddressFamily.InterNetwork);
+            Assert.IsNotNull(ipv4, $@"No IPv4 address found for '{hostName}'.");
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(200);
+                client.Connect();
+
+                var forwardedPort = new ForwardedPortDynamic(1080);
+                forwardedPort.Exception += (sender, args) => Console.WriteLine(args.Exception.ToString());
+                client.AddForwardedPort(forwardedPort);
+                forwardedPort.Start();
+
+                var socksClient = new Socks5Handler(new IPEndPoint(IPAddress.Loopback, 1080),
+                                                    string.Empty,
+                                                    string.Empty);
+                var socksSocket = socksClient.Connect(new IPEndPoint(ipv4, 80));
+
+                socksSocket.Send(httpGetRequest);
+                var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII);
+                Assert.IsTrue(httpResponse.Contains(searchText), httpResponse);
+
+                forwardedPort.Dispose();
+
+                // check if client socket was properly closed
+                Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None));
+            }
+        }
+
+        /// <summary>
+        /// Verifies whether channels are effectively closed.
+        /// </summary>
+        [TestMethod]
+        public void Ssh_LocalPortForwardingCloseChannels()
+        {
+            const string hostNameAlias = "localportforwarding-test.for.sshnet";
+            const string hostName = "github.com";
+
+            var ipAddress = Dns.GetHostAddresses(hostName)[0];
+
+            var hostsFileUpdated = AddOrUpdateHostsEntry(_adminConnectionInfoFactory, ipAddress, hostNameAlias);
+
+            try
+            {
+                var connectionInfo = _connectionInfoFactory.Create();
+                connectionInfo.MaxSessions = 1;
+
+                using (var client = new SshClient(connectionInfo))
+                {
+                    client.Connect();
+
+                    var localEndPoint = new IPEndPoint(IPAddress.Loopback, 1225);
+
+                    for (var i = 0; i < (connectionInfo.MaxSessions + 1); i++)
+                    {
+                        var forwardedPort = new ForwardedPortLocal(localEndPoint.Address.ToString(),
+                                                                   (uint)localEndPoint.Port,
+                                                                   hostNameAlias,
+                                                                   80);
+                        client.AddForwardedPort(forwardedPort);
+                        forwardedPort.Start();
+
+                        try
+                        {
+                            var httpRequest = (HttpWebRequest) WebRequest.Create("http://" + localEndPoint);
+                            httpRequest.Host = hostName;
+                            httpRequest.Method = "GET";
+                            httpRequest.AllowAutoRedirect = false;
+
+                            try
+                            {
+                                using (var httpResponse = (HttpWebResponse)httpRequest.GetResponse())
+                                {
+                                    Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode);
+                                }
+                            }
+                            catch (WebException ex)
+                            {
+                                Assert.AreEqual(WebExceptionStatus.ProtocolError, ex.Status);
+                                Assert.IsNotNull(ex.Response);
+
+                                using (var httpResponse = ex.Response as HttpWebResponse)
+                                {
+                                    Assert.IsNotNull(httpResponse);
+                                    Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode);
+                                }
+                            }
+                        }
+                        finally
+                        {
+                            client.RemoveForwardedPort(forwardedPort);
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                if (hostsFileUpdated)
+                {
+                    RemoveHostsEntry(_adminConnectionInfoFactory, ipAddress, hostNameAlias);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Ssh_LocalPortForwarding()
+        {
+            const string hostNameAlias = "localportforwarding-test.for.sshnet";
+            const string hostName = "github.com";
+
+            var ipAddress = Dns.GetHostAddresses(hostName)[0];
+
+            var hostsFileUpdated = AddOrUpdateHostsEntry(_adminConnectionInfoFactory, ipAddress, hostNameAlias);
+
+            try
+            {
+                using (var client = new SshClient(_connectionInfoFactory.Create()))
+                {
+                    client.Connect();
+
+                    var localEndPoint = new IPEndPoint(IPAddress.Loopback, 1225);
+
+                    var forwardedPort = new ForwardedPortLocal(localEndPoint.Address.ToString(),
+                                                               (uint)localEndPoint.Port,
+                                                               hostNameAlias,
+                                                               80);
+                    forwardedPort.Exception +=
+                        (sender, args) => Console.WriteLine(@"ForwardedPort exception: " + args.Exception);
+                    client.AddForwardedPort(forwardedPort);
+                    forwardedPort.Start();
+
+                    try
+                    {
+                        var httpRequest = (HttpWebRequest) WebRequest.Create("http://" + localEndPoint);
+                        httpRequest.Host = hostName;
+                        httpRequest.Method = "GET";
+                        httpRequest.Accept = "text/html";
+                        httpRequest.AllowAutoRedirect = false;
+
+                        try
+                        {
+                            using (var httpResponse = (HttpWebResponse)httpRequest.GetResponse())
+                            {
+                                Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode);
+                            }
+                        }
+                        catch (WebException ex)
+                        {
+                            Assert.AreEqual(WebExceptionStatus.ProtocolError, ex.Status);
+                            Assert.IsNotNull(ex.Response);
+
+                            using (var httpResponse = ex.Response as HttpWebResponse)
+                            {
+                                Assert.IsNotNull(httpResponse);
+                                Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode);
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        client.RemoveForwardedPort(forwardedPort);
+                    }
+                }
+            }
+            finally
+            {
+                if (hostsFileUpdated)
+                {
+                    RemoveHostsEntry(_adminConnectionInfoFactory, ipAddress, hostNameAlias);
+                }
+            }
+        }
+
+        [TestMethod]
+        public void Ssh_RemotePortForwarding()
+        {
+            var hostAddresses = Dns.GetHostAddresses(Dns.GetHostName());
+            var ipv4HostAddress = hostAddresses.First(p => p.AddressFamily == AddressFamily.InterNetwork);
+
+            var endpoint1 = new IPEndPoint(ipv4HostAddress, 666);
+            var endpoint2 = new IPEndPoint(ipv4HostAddress, 667);
+
+            var bytesReceivedOnListener1 = new List<byte>();
+            var bytesReceivedOnListener2 = new List<byte>();
+
+            using (var socketListener1 = new AsyncSocketListener(endpoint1))
+            using (var socketListener2 = new AsyncSocketListener(endpoint2))
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                socketListener1.BytesReceived += (received, socket) => bytesReceivedOnListener1.AddRange(received);
+                socketListener1.Start();
+
+                socketListener2.BytesReceived += (received, socket) => bytesReceivedOnListener2.AddRange(received);
+                socketListener2.Start();
+
+                client.Connect();
+
+                var forwardedPort1 = new ForwardedPortRemote(IPAddress.Loopback,
+                                                             10000,
+                                                             endpoint1.Address,
+                                                             (uint)endpoint1.Port);
+                forwardedPort1.Exception += (sender, args) => Console.WriteLine(@"forwardedPort1 exception: " + args.Exception);
+                client.AddForwardedPort(forwardedPort1);
+                forwardedPort1.Start();
+
+                var forwardedPort2 = new ForwardedPortRemote(IPAddress.Loopback,
+                                                             10001,
+                                                             endpoint2.Address,
+                                                             (uint)endpoint2.Port);
+                forwardedPort2.Exception += (sender, args) => Console.WriteLine(@"forwardedPort2 exception: " + args.Exception);
+                client.AddForwardedPort(forwardedPort2);
+                forwardedPort2.Start();
+
+                using (var s = client.CreateShellStream("a", 80, 25, 800, 600, 200))
+                {
+                    s.WriteLine($"telnet {forwardedPort1.BoundHost} {forwardedPort1.BoundPort}");
+                    s.Expect($"Connected to {forwardedPort1.BoundHost}\r\n");
+                    s.WriteLine("ABC");
+                    s.Flush();
+                    s.Expect("ABC");
+                    s.Close();
+                }
+
+                using (var s = client.CreateShellStream("b", 80, 25, 800, 600, 200))
+                {
+                    s.WriteLine($"telnet {forwardedPort2.BoundHost} {forwardedPort2.BoundPort}");
+                    s.Expect($"Connected to {forwardedPort2.BoundHost}\r\n");
+                    s.WriteLine("DEF");
+                    s.Flush();
+                    s.Expect("DEF");
+                    s.Close();
+                }
+
+                forwardedPort1.Stop();
+                forwardedPort2.Stop();
+            }
+
+            var textReceivedOnListener1 = Encoding.ASCII.GetString(bytesReceivedOnListener1.ToArray());
+            Assert.AreEqual("ABC\r\n", textReceivedOnListener1);
+
+            var textReceivedOnListener2 = Encoding.ASCII.GetString(bytesReceivedOnListener2.ToArray());
+            Assert.AreEqual("DEF\r\n", textReceivedOnListener2);
+        }
+
+        /// <summary>
+        /// Issue 1591
+        /// </summary>
+        [TestMethod]
+        public void Ssh_ExecuteShellScript()
+        {
+            const string remoteFile = "/home/sshnet/run.sh";
+            const string content = "#\bin\bash\necho Hello World!";
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                if (client.Exists(remoteFile))
+                {
+                    client.DeleteFile(remoteFile);
+                }
+
+                using (var memoryStream = new MemoryStream())
+                using (var sw = new StreamWriter(memoryStream, Encoding.ASCII))
+                {
+                    sw.Write(content);
+                    sw.Flush();
+                    memoryStream.Position = 0;
+                    client.UploadFile(memoryStream, remoteFile);
+                }
+            }
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                try
+                {
+                    var runChmod = client.RunCommand("chmod u+x " + remoteFile);
+                    runChmod.Execute();
+                    Assert.AreEqual(0, runChmod.ExitStatus, runChmod.Error);
+
+                    var runLs = client.RunCommand("ls " + remoteFile);
+                    var asyncResultLs = runLs.BeginExecute();
+
+                    var runScript = client.RunCommand(remoteFile);
+                    var asyncResultScript = runScript.BeginExecute();
+
+                    Assert.IsTrue(asyncResultScript.AsyncWaitHandle.WaitOne(10000));
+                    var resultScript = runScript.EndExecute(asyncResultScript);
+                    Assert.AreEqual("Hello World!\n", resultScript);
+
+                    Assert.IsTrue(asyncResultLs.AsyncWaitHandle.WaitOne(10000));
+                    var resultLs = runLs.EndExecute(asyncResultLs);
+                    Assert.AreEqual(remoteFile + "\n", resultLs);
+                }
+                finally
+                {
+                    RemoveFileOrDirectory(client, remoteFile);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Verifies if a hosts file contains an entry for a given combination of IP address and hostname,
+        /// and if necessary add either the host entry or an alias to an exist entry for the specified IP
+        /// address.
+        /// </summary>
+        /// <param name="linuxAdminConnectionFactory"></param>
+        /// <param name="ipAddress"></param>
+        /// <param name="hostName"></param>
+        /// <returns>
+        /// <see langword="true"/> if an entry was added or updated in the specified hosts file; otherwise,
+        /// <see langword="false"/>.
+        /// </returns>
+        private static bool AddOrUpdateHostsEntry(IConnectionInfoFactory linuxAdminConnectionFactory,
+                                                  IPAddress ipAddress,
+                                                  string hostName)
+        {
+            const string hostsFile = "/etc/hosts";
+
+            using (var client = new ScpClient(linuxAdminConnectionFactory.Create()))
+            {
+                client.Connect();
+
+                var hostConfig = HostConfig.Read(client, hostsFile);
+
+                var hostEntry = hostConfig.Entries.SingleOrDefault(h => h.IPAddress.Equals(ipAddress));
+                if (hostEntry != null)
+                {
+                    if (hostEntry.HostName == hostName)
+                    {
+                        return false;
+                    }
+
+                    foreach (var alias in hostEntry.Aliases)
+                    {
+                        if (alias == hostName)
+                        {
+                            return false;
+                        }
+                    }
+
+                    hostEntry.Aliases.Add(hostName);
+                }
+                else
+                {
+                    bool mappingFound = false;
+
+                    for (var i = (hostConfig.Entries.Count - 1); i >= 0; i--)
+                    {
+                        hostEntry = hostConfig.Entries[i];
+
+                        if (hostEntry.HostName == hostName)
+                        {
+                            if (hostEntry.IPAddress.Equals(ipAddress))
+                            {
+                                mappingFound = true;
+                                continue;
+                            }
+
+                            // If hostname is currently mapped to another IP address, then remove the 
+                            // current mapping
+                            hostConfig.Entries.RemoveAt(i);
+                        }
+                        else
+                        {
+                            for (var j = (hostEntry.Aliases.Count - 1); j >= 0; j--)
+                            {
+                                var alias = hostEntry.Aliases[j];
+
+                                if (alias == hostName)
+                                {
+                                    hostEntry.Aliases.RemoveAt(j);
+                                }
+                            }
+                        }
+                    }
+
+                    if (!mappingFound)
+                    {
+                        hostEntry = new HostEntry(ipAddress, hostName);
+                        hostConfig.Entries.Add(hostEntry);
+                    }
+                }
+
+                hostConfig.Write(client, hostsFile);
+                return true;
+            }
+        }
+
+        /// <summary>
+        /// Remove the mapping between a given IP address and host name from the remote hosts file either by
+        /// removing a host entry entirely (if no other aliases are defined for the IP address) or removing
+        /// the aliases that match the host name for the IP address.
+        /// </summary>
+        /// <param name="linuxAdminConnectionFactory"></param>
+        /// <param name="ipAddress"></param>
+        /// <param name="hostName"></param>
+        /// <returns>
+        /// <see langword="true"/> if the hosts file was updated; otherwise, <see langword="false"/>.
+        /// </returns>
+        private static bool RemoveHostsEntry(IConnectionInfoFactory linuxAdminConnectionFactory,
+                                             IPAddress ipAddress,
+                                             string hostName)
+        {
+            const string hostsFile = "/etc/hosts";
+
+            using (var client = new ScpClient(linuxAdminConnectionFactory.Create()))
+            {
+                client.Connect();
+
+                var hostConfig = HostConfig.Read(client, hostsFile);
+
+                var hostEntry = hostConfig.Entries.SingleOrDefault(h => h.IPAddress.Equals(ipAddress));
+                if (hostEntry == null)
+                {
+                    return false;
+                }
+
+                if (hostEntry.HostName == hostName)
+                {
+                    if (hostEntry.Aliases.Count == 0)
+                    {
+                        hostConfig.Entries.Remove(hostEntry);
+                    }
+                    else
+                    {
+                        // Use one of the aliases (that are different from the specified host name) as host name
+                        // of the host entry.
+
+                        for (var i = hostEntry.Aliases.Count - 1; i >= 0; i--)
+                        {
+                            var alias = hostEntry.Aliases[i];
+                            if (alias == hostName)
+                            {
+                                hostEntry.Aliases.RemoveAt(i);
+                            }
+                            else if (hostEntry.HostName == hostName)
+                            {
+                                // If we haven't already used one of the aliases as host name of the host entry
+                                // then do this now and remove the alias.
+
+                                hostEntry.HostName = alias;
+                                hostEntry.Aliases.RemoveAt(i);
+                            }
+                        }
+
+                        // If for some reason the host name of the host entry matched the specified host name
+                        // and it only had aliases that match the host name, then remove the host entry altogether.
+                        if (hostEntry.Aliases.Count == 0 && hostEntry.HostName == hostName)
+                        {
+                            hostConfig.Entries.Remove(hostEntry);
+                        }
+                    }
+                }
+                else
+                {
+                    var aliasRemoved = false;
+
+                    for (var i = hostEntry.Aliases.Count - 1; i >= 0; i--)
+                    {
+                        if (hostEntry.Aliases[i] == hostName)
+                        {
+                            hostEntry.Aliases.RemoveAt(i);
+                            aliasRemoved = true;
+                        }
+                    }
+
+                    if (!aliasRemoved)
+                    {
+                        return false;
+                    }
+                }
+
+                hostConfig.Write(client, hostsFile);
+                return true;
+            }
+        }
+
+        private static string GetHttpResponse(Socket socket, Encoding encoding)
+        {
+            var httpResponseBuffer = new byte[2048];
+
+            // We expect:
+            // * The response to contain the searchText in the first receive.
+            // * The full response to be returned in the first receive.
+
+            var bytesReceived = socket.Receive(httpResponseBuffer,
+                                               0,
+                                               httpResponseBuffer.Length,
+                                               SocketFlags.None);
+            if (bytesReceived == 0)
+            {
+                return null;
+            }
+
+            if (bytesReceived == httpResponseBuffer.Length)
+            {
+                throw new Exception("We expect the HTTP response to be less than the buffer size. If not, we won't consume the full response.");
+            }
+
+            using (var sr = new StringReader(encoding.GetString(httpResponseBuffer, 0, bytesReceived)))
+            {
+                return sr.ReadToEnd();
+            }
+        }
+
+        private static void CreateShellScript(IConnectionInfoFactory connectionInfoFactory, string remoteFile, string script)
+        {
+            using (var sftpClient = new SftpClient(connectionInfoFactory.Create()))
+            {
+                sftpClient.Connect();
+                
+                using (var sw = sftpClient.CreateText(remoteFile, new UTF8Encoding(false)))
+                {
+                    sw.Write(script);
+                }
+
+                sftpClient.ChangePermissions(remoteFile, 0x1FF);
+            }
+        }
+
+        private static void RemoveFileOrDirectory(SshClient client, string remoteFile)
+        {
+            using (var cmd = client.CreateCommand("rm -Rf " + remoteFile))
+            {
+                cmd.Execute();
+                Assert.AreEqual(0, cmd.ExitStatus, cmd.Error);
+            }
+        }
+    }
+}

+ 80 - 0
src/Renci.SshNet.IntegrationTests/TestBase.cs

@@ -0,0 +1,80 @@
+using System.Security.Cryptography;
+
+namespace Renci.SshNet.IntegrationTests
+{
+    public abstract class TestBase : IntegrationTestBase
+    {
+        protected static MemoryStream CreateMemoryStream(int size)
+        {
+            var memoryStream = new MemoryStream();
+            FillStream(memoryStream, size);
+            return memoryStream;
+        }
+
+        protected static void FillStream(Stream stream, int size)
+        {
+            var randomContent = new byte[50];
+            var random = new Random();
+
+            var numberOfBytesToWrite = size;
+
+            while (numberOfBytesToWrite > 0)
+            {
+                random.NextBytes(randomContent);
+
+                var numberOfCharsToWrite = Math.Min(numberOfBytesToWrite, randomContent.Length);
+                stream.Write(randomContent, 0, numberOfCharsToWrite);
+                numberOfBytesToWrite -= numberOfCharsToWrite;
+            }
+        }
+
+        protected static string CreateHash(Stream stream)
+        {
+            MD5 md5 = new MD5CryptoServiceProvider();
+            var hash = md5.ComputeHash(stream);
+            return Encoding.ASCII.GetString(hash);
+        }
+
+        protected static string CreateHash(byte[] buffer)
+        {
+            using (var ms = new MemoryStream(buffer))
+            {
+                return CreateHash(ms);
+            }
+        }
+
+        protected static string CreateFileHash(string path)
+        {
+            using (var fs = File.OpenRead(path))
+            {
+                return CreateHash(fs);
+            }
+        }
+
+        protected static string CreateTempFile(int size)
+        {
+            var file = Path.GetTempFileName();
+            CreateFile(file, size);
+            return file;
+        }
+
+        protected static void CreateFile(string fileName, int size)
+        {
+            using (var fs = File.OpenWrite(fileName))
+            {
+                FillStream(fs, size);
+            }
+        }
+
+        protected Stream GetManifestResourceStream(string resourceName)
+        {
+            var type = GetType();
+            var resourceStream = type.Assembly.GetManifestResourceStream(resourceName);
+            if (resourceStream == null)
+            {
+                throw new ArgumentException($"Resource '{resourceName}' not found in assembly '{type.Assembly.FullName}'.", nameof(resourceName));
+            }
+            return resourceStream;
+        }
+    }
+}

+ 1 - 1
src/Renci.SshNet.IntegrationTests/TestInitializer.cs

@@ -1,4 +1,4 @@
-namespace IntegrationTests
+namespace Renci.SshNet.IntegrationTests
 {
     [TestClass]
     public class TestInitializer

+ 6 - 6
src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs

@@ -1,8 +1,8 @@
-using DotNet.Testcontainers.Images;
-using DotNet.Testcontainers.Builders;
+using DotNet.Testcontainers.Builders;
 using DotNet.Testcontainers.Containers;
+using DotNet.Testcontainers.Images;
 
-namespace IntegrationTests.TestsFixtures
+namespace Renci.SshNet.IntegrationTests.TestsFixtures
 {
     public sealed class InfrastructureFixture : IDisposable
     {
@@ -20,11 +20,11 @@ namespace IntegrationTests.TestsFixtures
             }
         }
 
-        private IContainer? _sshServer;
+        private IContainer _sshServer;
 
-        private IFutureDockerImage? _sshServerImage;
+        private IFutureDockerImage _sshServerImage;
 
-        public string? SshServerHostName { get; set; }
+        public string SshServerHostName { get; set; }
 
         public ushort SshServerPort { get; set; }
 

+ 21 - 2
src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs

@@ -1,4 +1,4 @@
-namespace IntegrationTests.TestsFixtures
+namespace Renci.SshNet.IntegrationTests.TestsFixtures
 {
     /// <summary>
     /// The base class for integration tests
@@ -10,7 +10,7 @@
         /// <summary>
         /// The SSH Server host name.
         /// </summary>
-        public string? SshServerHostName
+        public string SshServerHostName
         {
             get
             {
@@ -62,5 +62,24 @@
             Console.WriteLine($"SSH Server host name: {_infrastructureFixture.SshServerHostName}");
             Console.WriteLine($"SSH Server port: {_infrastructureFixture.SshServerPort}");
         }
+
+        /// <summary>
+        /// Creates the test file.
+        /// </summary>
+        /// <param name="fileName">Name of the file.</param>
+        /// <param name="size">Size in megabytes.</param>
+        protected void CreateTestFile(string fileName, int size)
+        {
+            using (var testFile = File.Create(fileName))
+            {
+                var random = new Random();
+                for (int i = 0; i < 1024 * size; i++)
+                {
+                    var buffer = new byte[1024];
+                    random.NextBytes(buffer);
+                    testFile.Write(buffer, 0, buffer.Length);
+                }
+            }
+        }
     }
 }

+ 1 - 1
src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs

@@ -1,4 +1,4 @@
-namespace IntegrationTests.TestsFixtures
+namespace Renci.SshNet.IntegrationTests.TestsFixtures
 {
     public class SshUser
     {

+ 8 - 0
src/Renci.SshNet.IntegrationTests/Users.cs

@@ -0,0 +1,8 @@
+namespace Renci.SshNet.IntegrationTests
+{
+    internal static class Users
+    {
+        public static readonly Credential Regular = new Credential("sshnet", "ssh4ever");
+        public static readonly Credential Admin = new Credential("sshnetadm", "ssh4ever");
+    }
+}

+ 1 - 5
src/Renci.SshNet.IntegrationTests/Usings.cs

@@ -1,9 +1,5 @@
-#pragma warning disable IDE0005
-
 global using System.Text;
 
 global using Microsoft.VisualStudio.TestTools.UnitTesting;
 
-global using IntegrationTests.TestsFixtures;
-
-
+global using Renci.SshNet.IntegrationTests.TestsFixtures;

+ 12 - 0
src/Renci.SshNet.IntegrationTests/resources/client/id_dsa

@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQC1Zd32ntjuKsLACveUlEoV9CjFDT/spf7k95Rh/U/2abZx0pa0
+8Z01lwpdIPdShHgmhJww4S8ZuGgr9QIOAf8r3DOLt+KpJmjS8ti4gMYqnaG2XDRu
+tT6sKneSA5Kd/CwbJ/LZm9dsbvTWpaVHzokhAdXMgq+MxWTK5tMLXciUFQIVAK76
+I2Sp/9g4BiNisdIIcWZYB8RhAoGADlSeN+FAEdx5+pQOZ1jXxTrlFR91u5yWj9BU
+CYiD8exlG3cTvarQzU21pFi93PasefgezpXuMTO3L8lz6zUFGAxwhZUvlHtsdyHi
+a5HX2ZB/Xjz9ucuQNCeP3PvF170Go+MwOZ38Nd6MuT7cne3dyqubRAzPColXSIcJ
+F41ANz0CgYEAm8IGZQatS7M6AfNITNWG4TI7Z2aRQjLb9/MWJIID7c/VQ4zdTZdG
+3kpk0Gj9n4xreopK5NmYAdj8rtFfPBgmXltsLqt+bBcXkpxW//7WC29WOXW3t90y
+STh+cWuWfr9fV7mf4Ql/6u/ZIgpQNvnNYezazt3fK8EXjI1dAXEuQxECFBhGOzk+
+Aimeob964E8+HsQNlyde
+-----END DSA PRIVATE KEY-----

+ 17 - 0
src/Renci.SshNet.IntegrationTests/resources/client/id_dsa.ppk

@@ -0,0 +1,17 @@
+PuTTY-User-Key-File-2: ssh-dss
+Encryption: none
+Comment: imported-openssh-key
+Public-Lines: 10
+AAAAB3NzaC1kc3MAAACBALVl3fae2O4qwsAK95SUShX0KMUNP+yl/uT3lGH9T/Zp
+tnHSlrTxnTWXCl0g91KEeCaEnDDhLxm4aCv1Ag4B/yvcM4u34qkmaNLy2LiAxiqd
+obZcNG61Pqwqd5IDkp38LBsn8tmb12xu9NalpUfOiSEB1cyCr4zFZMrm0wtdyJQV
+AAAAFQCu+iNkqf/YOAYjYrHSCHFmWAfEYQAAAIAOVJ434UAR3Hn6lA5nWNfFOuUV
+H3W7nJaP0FQJiIPx7GUbdxO9qtDNTbWkWL3c9qx5+B7Ole4xM7cvyXPrNQUYDHCF
+lS+Ue2x3IeJrkdfZkH9ePP25y5A0J4/c+8XXvQaj4zA5nfw13oy5Ptyd7d3Kq5tE
+DM8KiVdIhwkXjUA3PQAAAIEAm8IGZQatS7M6AfNITNWG4TI7Z2aRQjLb9/MWJIID
+7c/VQ4zdTZdG3kpk0Gj9n4xreopK5NmYAdj8rtFfPBgmXltsLqt+bBcXkpxW//7W
+C29WOXW3t90ySTh+cWuWfr9fV7mf4Ql/6u/ZIgpQNvnNYezazt3fK8EXjI1dAXEu
+QxE=
+Private-Lines: 1
+AAAAFBhGOzk+Aimeob964E8+HsQNlyde
+Private-MAC: 1c254f3882a6661c98fb82dea1a55638a23633e5

+ 27 - 0
src/Renci.SshNet.IntegrationTests/resources/client/id_noaccess.rsa

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEoQIBAAKCAQEAuTtXn+BatX1oJuvhqfJZw5jc/pcIxJUPmuoFCH3+bXfKBJ/9
+4ixNETzZBasyvT/ozboAbCG3qcJOYxf2BEeTAIXe1jLAoTd1GKCwMvZOyjnsPN95
+/lChwfdnBbMzpZYTGfoUylXme/mzjjLu/J0qXgR5lyk9HFT+x5YEtRl8VSHiDkLK
+TZ37dwhsqgcs+PkfvYMUK+C8evnfE0tgWgKZk0Eatl87nLWyVXB4LzhSDtGKLCPA
+OgrX7fYfplDwJ2WK1N6nG0FnxW1HhDeSK7e2TbAa2vZQgvFXMWnO4O/NZKp4COpO
+ReyliWhdtKAjr/+cD4yDfPjhjjKOYfxbvdRG4QIBIwKCAQAqVrTxV9o4HKoXhl93
+TVZYl/f/rX5Y0Z0quSW4zFdpendRg6e+qwpNFTjrWlS9ivNiOSSrAGR+ktAWpmQe
+PD7bjFAw9ahfXSIUQfxja3+5Mc+Y4p+KlhZYOIyTlqy4Ik2CR8o84G8yR7QDPteK
+Mo1XUXrguPgGedPV2SWlvK60XyAXqsewDhi7SeImZomKzbh33SXjVxakzHfa8BEU
+eIIeR9oFlQMuYdo4GrHhFO2T+g/gqw/kVd1zkeEwt06fZVDErVwp+twewxxvwrk4
+CKUCzavfhDfi5sJ5YdzhDBRgkyBgJI+f15dKyqqOiAparV9+uzrD6vIuNnlVoqQA
+iugLAoGBAPBliy32e83nshBknBn5HOK2rO3a1zHxvYr/NzITXtdZOjatNyfXtkwi
+Ll/el5tZhJvKe9nItSI/4w7mvlvXZfW8h3MR0qb8at4jWa8ya2hwEerqaJonqjjb
++eBhg27ltZIQRk8Bv6ApXTAWkc+dFGhEIysokDQX7V72Bdrizup1AoGBAMVBLHK0
+5IFb8x7danlAmDX6bqCObId4Pce2OeONFIj1jIowvCXaE0t9zU4X5SdN5ujqu4Dq
+XgzUdNeKcJxWpFO74MDRxT3CbMz36fikJnvxWl/+q0HalYuCY8gm14VYcThUBAro
+3c941INueybGNLIA9jc7RMnsFtyVTvNYpaU9AoGAFJr9TRUgjf3qsPKuS15+0Zqh
+G7OsC5hgtCSBEuu3rA72XHU/Pe3rDdcLSgvD2h2dpvQZPo2L3l0/WQx2t2o78H3f
+uWftfAcB2Iav6nIJNNZn75BvXaug4E1ej5NUaJdYtL+Q/3UtrqR1s6opwVabWWTt
+ElPvGmhzboodwk30en8CgYAyuPzNCfGdm00lMZ8JPH7pTwaBDq4xdrDM9FgHUCna
+E0FlXP0uTgT2J6nSQKijtPI75JadfhgvL1E+vTLmX2wViBU45XvcrlZ92Vlr0nBL
+wbgnUB1otIzauyD49AuIsFegxSWcZ8QCJmKIMlouir0X1FyR3Apfzv6Qfio+kyNH
+vwKBgQCtwxojkzUSfV3zDt6bYSLBzgXgo/Zr9lS+gSggP72DzINmW2gbA0fkM2Zu
+JltcfakKv4gVX/1zooz+7t+4bj6dqt+bl7hYz0VnTSDZGuo5LKDif/4gSGrdblC2
+QLTuX2HjWCZdsue7mRwL7cXR4zlIoE99+Ryhdxvc5wHSfYr/JA==
+-----END RSA PRIVATE KEY-----

+ 27 - 0
src/Renci.SshNet.IntegrationTests/resources/client/id_rsa

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuDdE+laKZ8D0mEFMNMGkDYQY89A2BUML5L+DsFmEn//3yRdz
+Omx+Nk59ifiJUPuP3whZxN5vfbHleU9ZshI+37LqTbXmNrChRlxDSxrQA/++hTNI
+gZl8e+sZUk8mUkpjQyEVOMVEdt3H3PmpfOBeZjtpRJPuWa90J+SVKbnAX4rOL7bt
+LDA4GeMNXdLXLl5E/zBCZ9ol9nJ8W0suajQDx7u2/ixH1wb9jqzA6j68f1+kERII
+t8OUcS+ZM+274rVrCMCL290k9gEQAKwcG/KRMqdgP8Oadtq5ELS/t4m2fH5JqYJ9
+9399QGT4LTPvoiTwZjyhYwK3FhCfXyFQf+gwIwIDAQABAoIBAGLChsFrKfJr2PXT
+dAaIleoFGteDlaKGilbNcc1WgKrCsNXnM4hr59I3jEgurXd0FnKs6GuKEN2jRPIf
+X2f/LiQBqGmXDl/dm+i7x/v42PJ75mlE0Cdi4QESTlX5RwMxDDxN/TGdWJIdXmwS
+kRH4u8M1ML9qS4tba/uDKZDgG8lcF/z6K0RbXDMxL0azfbd5e+jca1e8Fs93X5s7
+mJotXaA+L33R7lCpBBOa1OY517Ug7bdI+uWh59o0bw8v2q8vr8ISOHU3N+JvKZ62
+2z5O6lLyB94sF6ltXRv9pmfcSBjfB8CJx5q1yejUZKM6VfN98MTSKo8WKMEtGmqk
+BtZFTlECgYEA8WYG6vntqXPCHhFFfgCymh8OoXL/mPDSkqsswKfeD4O+Ml9hRUtg
+xl6FDxFAMR7WJ4Vb8u9IMOc7Xx+nzlZNsdC5m7FAVRIEUPPjEucte+ZYKjSy+WOd
+dAtQ07O3z9fi+JNplSjisKtBWaemfqc2TcYXOeIIgwJnkaCf2C7I2bsCgYEAw1vJ
+9c5VLTisPj7ijMeGLWISG5E0aidOrb15E8xcnXuT9TEW1Dc1EgRK/D03tlnoxr1I
+CISPx4EmdLTiEl2AVi33DOhCeFAt8TOd/y3chKsbewb+BYEMmBD93mhsKg+YmC5E
+284SCV3fCcyFJfo9oy5Z2tIELerjT0cnpHgPCLkCgYEAij3OemRUeTUklol3jXgi
+z+Y3P7gWreREAuBqSY4YujPNCRXcI43ORuu8MWvEohyxsYJKrO3hHrhdJNWBCMYd
+ylXo5UN1vwIJXL6+bIXdY1X/aXQyhmVItzr/t6z0997/STFKRrRaVahNTWWYEHH7
+xEBL7scF7tjCrQAaafgo558CgYBkrrm3ZU+grsSWj/JSe8I7QX/zlTJeQ0PZZv0v
+pvNUdowaoeISHSHM10mOFj7QTCYbxxGI0kkHmRgordCVhnrN74KTtGANgcUrul6D
+VS+BcG4JSeFBFPFYreko5shYJRGP3MjAP8Qr76Uzd6RnnkCGCS1mCTb+M0BTa2iS
+6w1UgQKBgQDQ2qV4s7xH3dixy4MWhDBFmrQlFpQkNNkrJ/ImHrxI2tFyNaq1fE+Q
+PrXJi8mjwb/ETVN2C5iBTtIyVg1pZk3YAWAvIt9SPaRVYQWj8IJeOTTaNEZEvp5K
+1LJBWO0ksgJK28f/z7FwejeGbBwg8ch9wVhtFwIV++rZ76smP7C+9Q==
+-----END RSA PRIVATE KEY-----

+ 1 - 0
src/Renci.SshNet.IntegrationTests/resources/client/id_rsa.pub

@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6VopnwPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5mO2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LEfXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQtL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAj

+ 28 - 0
src/Renci.SshNet.IntegrationTests/resources/client/id_rsa_with_pass

@@ -0,0 +1,28 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABC9v0UCCP
+T+1yNEu9m0w939AAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6Vopn
+wPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1
+myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5m
+O2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LE
+fXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQ
+tL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAjAAADwPBAapXaoQvm3O
+2i9sBnmO+d8kCdm2nhGbEXNzswb0toARYyx7/rPON15BHv470NLK4GjtxWb8SbkUBjWIXJ
+dpJR1feYAgJQ27yaU6SBEJvnQBFI3EvH+h9ykaikDP/SzgZuGup5NZIoB09PzCPk4SbAwn
+skFT3s1v3ufaULYTiAO2xtWzABkjxfw8HOo+PF3UqGIF4145kGLUT1pUN7iy5EQZA8evRb
+Yt/9+ChcyBgKONgXdpjLNf02XIM/jQofZkROBg7ZAKCjtL3yGpvtOpwzCKy1hWDmgjtkeK
+Xn84/qwXWEobBa2wrDQ4Mjj7AIimRsCciO05bVB5KtNjT+WzCalpTzfj2nazukteNRKTD3
+bQR0gLFfFX4/YodXmtu2n+0R2AKkdPW5ZhoEEpT1FjfYUImAuElSEy5FEeR3bwE5uGQkF4
+uIMJWD+89QxO9PWKTloVI2hrOF9/z+UzUi7p16FQFDlB82qCQAiaIOHgotgDn9+OwMw0ew
+Gu/D3T8ZpKXcTAxK1JeoDFh2h+CE37JDvftNIxIhTp7lrhCdioj1DSBorwfke/q4+OvLUH
+8SZ6ZgppHjJ4jg6lB9TWCpD5PECDW+NuQQwUb5V4NKoBqnJrPaNUSbI7SL5Pq3mOXXNRmv
+q1Va06CfcHL3JupICFifux5xBDlQY0foAt7DFOgA6qANaCnRl2H2oFsEdRhBbL6EPP0bAQ
+7PBvWJlt5Aqr1V7QzcCHNZcyGpjiHeXsQxSWXzb1xQprlrDSnlGZpusrBTcZTLWUtOAgqQ
+dNbUNeUq1E74rZ/RBiVliaLHNBKwTCE9KyZTl/DcfgInB7DDEd7Vlmujzar7xAXRJot7k2
+a12gT69eVsqiO5si23493JFl0JB5vbQvQWARAvcdNPDPiILoJVpbgyL9oMzVzTjJyqYS1x
+cHXKE+rL4ZHeQhcfySXplZlUY9ADVY5QywAj2kl+5gT3Fohu/95axF7w9dAHyMntxbVnA3
+r0zOmqbAKOYEYEXxZ+Vq09YdEyJh33V48PUmvCusWWyeKIfjO4R1nD2+7iyiF7joF7bOBm
+o0LXuJdr81BCMueryPUaqSRpGhg/P/nUqzxj7p0mAh9uUOr/CtOpbc6wNY70RgdiMGFWhz
+zhO54p7iEg2zZNUZ5zRM1hTBikTeYyInpw8IQiMv9GH7/9gWwqPsh0qRVjj5kjqjSu069d
+FeuGjsMGCnLLG6WvOQ9DgH9JR4cfinBL3JwtpHFBIHwhsh2EIuSseVHvQlFlEc1U+7Yoxc
+0dn1VVtg==
+-----END OPENSSH PRIVATE KEY-----

+ 9 - 0
src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh

@@ -0,0 +1,9 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
+1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQT886z6SLRIzRu7VNA6SSeKZCNNRPXe
+iutTik1T3RUEshgnTI/V3T/d5QurCQPvf2ob3+Rd4FhCsVCS9gilIhVsAAAAsH3KX4d9yl
++HAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU
+0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFW
+wAAAAgYxeSyo7MVNup52COOCarcvARKlWhKIP2CKzj4qa5/6EAAAAYc3NobmV0QFVidW50
+dTE5MTBEZXNrdG9w
+-----END OPENSSH PRIVATE KEY-----

+ 1 - 0
src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_256_openssh.pub

@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFWw= sshnet@Ubuntu1910Desktop

+ 10 - 0
src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh

@@ -0,0 +1,10 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS
+1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQS0rLvxzSomgC4UNulA7/jdABXTG9un
+zFazinvOughrumo0n/R1DoSRXY4bHooQdq02pTD6/DK3FzS7n4ouJi/LuJfX1EFxYMJzP2
+aYlT0rOgvvIuLv2Q4OzcdjV8mzSIEAAADo1T5ZUtU+WVIAAAATZWNkc2Etc2hhMi1uaXN0
+cDM4NAAAAAhuaXN0cDM4NAAAAGEEtKy78c0qJoAuFDbpQO/43QAV0xvbp8xWs4p7zroIa7
+pqNJ/0dQ6EkV2OGx6KEHatNqUw+vwytxc0u5+KLiYvy7iX19RBcWDCcz9mmJU9KzoL7yLi
+79kODs3HY1fJs0iBAAAAMQDgRb336Dk9e4VxOpSqnwBqHRsJ3QmSME9qMBvx5SXykHFAsK
+wzVKEvIQizmg/+sWcAAAAYc3NobmV0QFVidW50dTE5MTBEZXNrdG9wAQIDBAUGBw==
+-----END OPENSSH PRIVATE KEY-----

+ 1 - 0
src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_384_openssh.pub

@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ== sshnet@Ubuntu1910Desktop

+ 12 - 0
src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh

@@ -0,0 +1,12 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
+1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAgeFoEYBgUaOlJPnEYIPLSTwmxLRl
+EUVpVAOzww3q10fj/Tuppty/fRLcbMoeVzWKl8mjDbR+XOdaKDGo6xcHGsgByITz2/F9wr
+E8BHyFEPemg8h0DKLW0X55J+rnn3lE0a0jXKngJ3VLVcgKgXam7KtpoCWFx689jVCpTxWI
+GrkIvlkAAAEYr0LrEa9C6xEAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
+AAAIUEAIHhaBGAYFGjpST5xGCDy0k8JsS0ZRFFaVQDs8MN6tdH4/07qabcv30S3GzKHlc1
+ipfJow20flznWigxqOsXBxrIAciE89vxfcKxPAR8hRD3poPIdAyi1tF+eSfq5595RNGtI1
+yp4Cd1S1XICoF2puyraaAlhcevPY1QqU8ViBq5CL5ZAAAAQQ50pmBidmKTIknaRpdO5WIu
+nYEGMUkLqdZ0egk9Ggg63mOHiLykf+XWcGbHbHM95CISXhqlvMtCYeGwOpP6FoMGAAAAGH
+NzaG5ldEBVYnVudHUxOTEwRGVza3RvcAECAw==
+-----END OPENSSH PRIVATE KEY-----

+ 1 - 0
src/Renci.SshNet.IntegrationTests/resources/client/key_ecdsa_521_openssh.pub

@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ== sshnet@Ubuntu1910Desktop

+ 7 - 0
src/Renci.SshNet.IntegrationTests/resources/client/key_ed25519_openssh

@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAJDRj1Tk7s7ik4Bnx3L3tjEY+e8l4ZmYJFMovUaZia5QAAAKBKTcfHSk3H
+xwAAAAtzc2gtZWQyNTUxOQAAACAJDRj1Tk7s7ik4Bnx3L3tjEY+e8l4ZmYJFMovUaZia5Q
+AAAEBMMOaGa8RU8Vy0vLFlRT5iVSxl3ji9NBKaO/RS0aFL3QkNGPVOTuzuKTgGfHcve2MR
+j57yXhmZgkUyi9RpmJrlAAAAGHNzaG5ldEBVYnVudHUxOTEwRGVza3RvcAECAwQF
+-----END OPENSSH PRIVATE KEY-----

BIN
src/Renci.SshNet.IntegrationTests/resources/issue #70.png


+ 54 - 0
src/Renci.SshNet.TestTools.OpenSSH/Cipher.cs

@@ -0,0 +1,54 @@
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+    public class Cipher
+    {
+        public static readonly Cipher TripledesCbc = new Cipher("3des-cbc");
+        public static readonly Cipher Aes128Cbc = new Cipher("aes128-cbc");
+        public static readonly Cipher Aes192Cbc = new Cipher("aes192-cbc");
+        public static readonly Cipher Aes256Cbc = new Cipher("aes256-cbc");
+        public static readonly Cipher RijndaelCbc = new Cipher("rijndael-cbc@lysator.liu.se");
+        public static readonly Cipher Aes128Ctr = new Cipher("aes128-ctr");
+        public static readonly Cipher Aes192Ctr = new Cipher("aes192-ctr");
+        public static readonly Cipher Aes256Ctr = new Cipher("aes256-ctr");
+        public static readonly Cipher Aes128Gcm = new Cipher("aes128-gcm@openssh.com");
+        public static readonly Cipher Aes256Gcm = new Cipher("aes256-gcm@openssh.com");
+        public static readonly Cipher Arcfour = new Cipher("arcfour");
+        public static readonly Cipher Arcfour128 = new Cipher("arcfour128");
+        public static readonly Cipher Arcfour256 = new Cipher("arcfour256");
+        public static readonly Cipher BlowfishCbc = new Cipher("blowfish-cbc");
+        public static readonly Cipher Cast128Cbc = new Cipher("cast128-cbc");
+        public static readonly Cipher Chacha20Poly1305 = new Cipher("chacha20-poly1305@openssh.com");
+
+        public Cipher(string name)
+        {
+            Name = name;
+        }
+
+        public string Name { get; }
+
+        public override bool Equals(object? obj)
+        {
+            if (obj == null)
+            {
+                return false;
+            }
+
+            if (obj is Cipher otherCipher)
+            {
+                return otherCipher.Name == Name;
+            }
+
+            return false;
+        }
+
+        public override int GetHashCode()
+        {
+            return Name.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return Name;
+        }
+    }
+}

+ 20 - 0
src/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs

@@ -0,0 +1,20 @@
+namespace Renci.SshNet.TestTools.OpenSSH.Formatters
+{
+    internal class BooleanFormatter
+    {
+        public string Format(bool value)
+        {
+            return value ? "yes" : "no";
+        }
+
+        public string Format(bool? value, bool defaultValue)
+        {
+            if (value.HasValue)
+            {
+                return Format(value.Value);
+            }
+
+            return Format(defaultValue);
+        }
+    }
+}

+ 12 - 0
src/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs

@@ -0,0 +1,12 @@
+using System.Globalization;
+
+namespace Renci.SshNet.TestTools.OpenSSH.Formatters
+{
+    internal class Int32Formatter
+    {
+        public string Format(int value)
+        {
+            return value.ToString(NumberFormatInfo.InvariantInfo);
+        }
+    }
+}

+ 10 - 0
src/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs

@@ -0,0 +1,10 @@
+namespace Renci.SshNet.TestTools.OpenSSH.Formatters
+{
+    internal class LogLevelFormatter
+    {
+        public string Format(LogLevel logLevel)
+        {
+            return logLevel.ToString("G").ToUpperInvariant();
+        }
+    }
+}

+ 54 - 0
src/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs

@@ -0,0 +1,54 @@
+namespace Renci.SshNet.TestTools.OpenSSH.Formatters
+{
+    internal class MatchFormatter
+    {
+        public string Format(Match match)
+        {
+            using (var writer = new StringWriter())
+            {
+                Format(match, writer);
+                return writer.ToString();
+            }
+        }
+
+        public void Format(Match match, TextWriter writer)
+        {
+            writer.Write("Match ");
+
+            if (match.Users.Length > 0)
+            {
+                writer.Write("User ");
+                for (var i = 0; i < match.Users.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        writer.Write(',');
+                    }
+
+                    writer.Write(match.Users[i]);
+                }
+            }
+
+            if (match.Addresses.Length > 0)
+            {
+                writer.Write("Address ");
+                for (var i = 0; i < match.Addresses.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        writer.Write(',');
+                    }
+
+                    writer.Write(match.Addresses[i]);
+                }
+            }
+
+            writer.WriteLine();
+
+            if (match.AuthenticationMethods != null)
+            {
+                writer.WriteLine("    AuthenticationMethods " + match.AuthenticationMethods);
+            }
+        }
+    }
+}

+ 10 - 0
src/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs

@@ -0,0 +1,10 @@
+namespace Renci.SshNet.TestTools.OpenSSH.Formatters
+{
+    internal class SubsystemFormatter
+    {
+        public string Format(Subsystem subsystem)
+        {
+            return subsystem.Name + " " + subsystem.Command;
+        }
+    }
+}

+ 53 - 0
src/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs

@@ -0,0 +1,53 @@
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+	public class HostKeyAlgorithm
+    {
+		public static readonly HostKeyAlgorithm EcdsaSha2Nistp256CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp256-cert-v01@openssh.com");
+		public static readonly HostKeyAlgorithm EcdsaSha2Nistp384CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp384-cert-v01@openssh.com");
+		public static readonly HostKeyAlgorithm EcdsaSha2Nistp521CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp521-cert-v01@openssh.com");
+		public static readonly HostKeyAlgorithm SshEd25519CertV01OpenSSH = new HostKeyAlgorithm("ssh-ed25519-cert-v01@openssh.com");
+		public static readonly HostKeyAlgorithm RsaSha2256CertV01OpenSSH = new HostKeyAlgorithm("rsa-sha2-256-cert-v01@openssh.com");
+		public static readonly HostKeyAlgorithm RsaSha2512CertV01OpenSSH = new HostKeyAlgorithm("rsa-sha2-512-cert-v01@openssh.com");
+		public static readonly HostKeyAlgorithm SshRsaCertV01OpenSSH = new HostKeyAlgorithm("ssh-rsa-cert-v01@openssh.com");
+		public static readonly HostKeyAlgorithm EcdsaSha2Nistp256 = new HostKeyAlgorithm("ecdsa-sha2-nistp256");
+		public static readonly HostKeyAlgorithm EcdsaSha2Nistp384 = new HostKeyAlgorithm("ecdsa-sha2-nistp384");
+		public static readonly HostKeyAlgorithm EcdsaSha2Nistp521 = new HostKeyAlgorithm("ecdsa-sha2-nistp521");
+		public static readonly HostKeyAlgorithm SshEd25519 = new HostKeyAlgorithm("ssh-ed25519");
+		public static readonly HostKeyAlgorithm RsaSha2512 = new HostKeyAlgorithm("rsa-sha2-512");
+		public static readonly HostKeyAlgorithm RsaSha2256 = new HostKeyAlgorithm("rsa-sha2-256");
+		public static readonly HostKeyAlgorithm SshRsa = new HostKeyAlgorithm("ssh-rsa");
+        public static readonly HostKeyAlgorithm SshDsa = new HostKeyAlgorithm("ssh-dsa");
+
+        public HostKeyAlgorithm(string name)
+        {
+			Name = name;
+		}
+
+		public string Name { get; }
+
+        public override bool Equals(object? obj)
+        {
+            if (obj == null)
+            {
+                return false;
+            }
+
+            if (obj is HostKeyAlgorithm otherHka)
+            {
+                return otherHka.Name == Name;
+            }
+
+            return false;
+        }
+
+        public override int GetHashCode()
+        {
+            return Name.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return Name;
+        }
+    }
+}

+ 52 - 0
src/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs

@@ -0,0 +1,52 @@
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+    public class KeyExchangeAlgorithm
+    {
+        public static readonly KeyExchangeAlgorithm DiffieHellmanGroup1Sha1 = new KeyExchangeAlgorithm("diffie-hellman-group1-sha1");
+        public static readonly KeyExchangeAlgorithm DiffieHellmanGroup14Sha1 = new KeyExchangeAlgorithm("diffie-hellman-group14-sha1");
+        public static readonly KeyExchangeAlgorithm DiffieHellmanGroup14Sha256 = new KeyExchangeAlgorithm("diffie-hellman-group14-sha256");
+        public static readonly KeyExchangeAlgorithm DiffieHellmanGroup16Sha512 = new KeyExchangeAlgorithm("diffie-hellman-group16-sha512");
+        public static readonly KeyExchangeAlgorithm DiffieHellmanGroup18Sha512 = new KeyExchangeAlgorithm("diffie-hellman-group18-sha512");
+        public static readonly KeyExchangeAlgorithm DiffieHellmanGroupExchangeSha1 = new KeyExchangeAlgorithm("diffie-hellman-group-exchange-sha1");
+        public static readonly KeyExchangeAlgorithm DiffieHellmanGroupExchangeSha256 = new KeyExchangeAlgorithm("diffie-hellman-group-exchange-sha256");
+        public static readonly KeyExchangeAlgorithm EcdhSha2Nistp256 = new KeyExchangeAlgorithm("ecdh-sha2-nistp256");
+        public static readonly KeyExchangeAlgorithm EcdhSha2Nistp384 = new KeyExchangeAlgorithm("ecdh-sha2-nistp384");
+        public static readonly KeyExchangeAlgorithm EcdhSha2Nistp521 = new KeyExchangeAlgorithm("ecdh-sha2-nistp521");
+        public static readonly KeyExchangeAlgorithm Curve25519Sha256 = new KeyExchangeAlgorithm("curve25519-sha256");
+        public static readonly KeyExchangeAlgorithm Curve25519Sha256Libssh = new KeyExchangeAlgorithm("curve25519-sha256@libssh.org");
+        public static readonly KeyExchangeAlgorithm Sntrup4591761x25519Sha512 = new KeyExchangeAlgorithm("sntrup4591761x25519-sha512@tinyssh.org");
+
+
+        public KeyExchangeAlgorithm(string name)
+        {
+            Name = name;
+        }
+
+        public string Name { get; }
+
+        public override bool Equals(object? obj)
+        {
+            if (obj == null)
+            {
+                return false;
+            }
+
+            if (obj is KeyExchangeAlgorithm otherKex)
+            {
+                return otherKex.Name == Name;
+            }
+
+            return false;
+        }
+
+        public override int GetHashCode()
+        {
+            return Name.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return Name;
+        }
+    }
+}

+ 15 - 0
src/Renci.SshNet.TestTools.OpenSSH/LogLevel.cs

@@ -0,0 +1,15 @@
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+    public enum LogLevel
+    {
+        Quiet = 1,
+        Fatal = 2,
+        Error = 3,
+        Info = 4,
+        Verbose = 5,
+        Debug = 6,
+        Debug1 = 7,
+        Debug2 = 8,
+        Debug3 = 9
+    }
+}

+ 57 - 0
src/Renci.SshNet.TestTools.OpenSSH/Match.cs

@@ -0,0 +1,57 @@
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+    public class Match
+    {
+        public Match(string[] users, string[] addresses)
+        {
+            Users = users;
+            Addresses = addresses;
+        }
+
+        public string[] Users { get; }
+
+        public string[] Addresses { get; }
+
+        public string? AuthenticationMethods { get; set; }
+
+        public void WriteTo(TextWriter writer)
+        {
+            writer.Write("Match ");
+
+            if (Users.Length > 0)
+            {
+                writer.Write("User ");
+                for (var i = 0; i < Users.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        writer.Write(',');
+                    }
+
+                    writer.Write(Users[i]);
+                }
+            }
+
+            if (Addresses.Length > 0)
+            {
+                writer.Write("Address ");
+                for (var i = 0; i < Addresses.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        writer.Write(',');
+                    }
+
+                    writer.Write(Addresses[i]);
+                }
+            }
+
+            writer.WriteLine();
+
+            if (AuthenticationMethods != null)
+            {
+                writer.WriteLine("    AuthenticationMethods " + AuthenticationMethods);
+            }
+        }
+    }
+}

+ 56 - 0
src/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs

@@ -0,0 +1,56 @@
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+    public class MessageAuthenticationCodeAlgorithm
+    {
+        public static readonly MessageAuthenticationCodeAlgorithm HmacMd5 = new MessageAuthenticationCodeAlgorithm("hmac-md5");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacMd5_96 = new MessageAuthenticationCodeAlgorithm("hmac-md5-96");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacRipemd160 = new MessageAuthenticationCodeAlgorithm("hmac-ripemd160");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacSha1 = new MessageAuthenticationCodeAlgorithm("hmac-sha1");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacSha1_96 = new MessageAuthenticationCodeAlgorithm("hmac-sha1-96");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_256 = new MessageAuthenticationCodeAlgorithm("hmac-sha2-256");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_512 = new MessageAuthenticationCodeAlgorithm("hmac-sha2-512");
+        public static readonly MessageAuthenticationCodeAlgorithm Umac64 = new MessageAuthenticationCodeAlgorithm("umac-64@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm Umac128 = new MessageAuthenticationCodeAlgorithm("umac-128@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacMd5Etm = new MessageAuthenticationCodeAlgorithm("hmac-md5-etm@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacMd5_96_Etm = new MessageAuthenticationCodeAlgorithm("hmac-md5-96-etm@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacRipemd160Etm = new MessageAuthenticationCodeAlgorithm("hmac-ripemd160-etm@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacSha1Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha1-etm@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacSha1_96_Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha1-96-etm@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_256_Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha2-256-etm@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_512_Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha2-512-etm@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm Umac64_Etm = new MessageAuthenticationCodeAlgorithm("umac-64-etm@openssh.com");
+        public static readonly MessageAuthenticationCodeAlgorithm Umac128_Etm = new MessageAuthenticationCodeAlgorithm("umac-128-etm@openssh.com");
+
+        public MessageAuthenticationCodeAlgorithm(string name)
+        {
+            Name = name;
+        }
+
+        public string Name { get; }
+
+        public override bool Equals(object? obj)
+        {
+            if (obj == null)
+            {
+                return false;
+            }
+
+            if (obj is MessageAuthenticationCodeAlgorithm otherMac)
+            {
+                return otherMac.Name == Name;
+            }
+
+            return false;
+        }
+
+        public override int GetHashCode()
+        {
+            return Name.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return Name;
+        }
+    }
+}

+ 59 - 0
src/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs

@@ -0,0 +1,59 @@
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+    public class PublicKeyAlgorithm
+    {
+        public static readonly PublicKeyAlgorithm SshEd25519 = new PublicKeyAlgorithm("ssh-ed25519");
+        public static readonly PublicKeyAlgorithm SshEd25519CertV01OpenSSH = new PublicKeyAlgorithm("ssh-ed25519-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm SkSshEd25519OpenSSH = new PublicKeyAlgorithm("sk-ssh-ed25519@openssh.com");
+        public static readonly PublicKeyAlgorithm SkSshEd25519CertV01OpenSSH = new PublicKeyAlgorithm("sk-ssh-ed25519-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm SshRsa = new PublicKeyAlgorithm("ssh-rsa");
+        public static readonly PublicKeyAlgorithm RsaSha2256 = new PublicKeyAlgorithm("rsa-sha2-256");
+        public static readonly PublicKeyAlgorithm RsaSha2512 = new PublicKeyAlgorithm("rsa-sha2-512");
+        public static readonly PublicKeyAlgorithm SshDss = new PublicKeyAlgorithm("ssh-dss");
+        public static readonly PublicKeyAlgorithm EcdsaSha2Nistp256 = new PublicKeyAlgorithm("ecdsa-sha2-nistp256");
+        public static readonly PublicKeyAlgorithm EcdsaSha2Nistp384 = new PublicKeyAlgorithm("ecdsa-sha2-nistp384");
+        public static readonly PublicKeyAlgorithm EcdsaSha2Nistp521 = new PublicKeyAlgorithm("ecdsa-sha2-nistp521");
+        public static readonly PublicKeyAlgorithm SkEcdsaSha2Nistp256OpenSSH = new PublicKeyAlgorithm("sk-ecdsa-sha2-nistp256@openssh.com");
+        public static readonly PublicKeyAlgorithm WebAuthnSkEcdsaSha2Nistp256OpenSSH = new PublicKeyAlgorithm("webauthn-sk-ecdsa-sha2-nistp256@openssh.com");
+        public static readonly PublicKeyAlgorithm SshRsaCertV01OpenSSH = new PublicKeyAlgorithm("ssh-rsa-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm RsaSha2256CertV01OpenSSH = new PublicKeyAlgorithm("rsa-sha2-256-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm RsaSha2512CertV01OpenSSH = new PublicKeyAlgorithm("rsa-sha2-512-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm SshDssCertV01OpenSSH = new PublicKeyAlgorithm("ssh-dss-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm EcdsaSha2Nistp256CertV01OpenSSH = new PublicKeyAlgorithm("ecdsa-sha2-nistp256-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm EcdsaSha2Nistp384CertV01OpenSSH = new PublicKeyAlgorithm("ecdsa-sha2-nistp384-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm EcdsaSha2Nistp521CertV01OpenSSH = new PublicKeyAlgorithm("ecdsa-sha2-nistp521-cert-v01@openssh.com");
+        public static readonly PublicKeyAlgorithm SkEcdsaSha2Nistp256CertV01OpenSSH = new PublicKeyAlgorithm("sk-ecdsa-sha2-nistp256-cert-v01@openssh.com");
+
+        public PublicKeyAlgorithm(string name)
+        {
+            Name = name;
+        }
+
+        public string Name { get; }
+
+        public override bool Equals(object? obj)
+        {
+            if (obj == null)
+            {
+                return false;
+            }
+
+            if (obj is HostKeyAlgorithm otherHka)
+            {
+                return otherHka.Name == Name;
+            }
+
+            return false;
+        }
+
+        public override int GetHashCode()
+        {
+            return Name.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return Name;
+        }
+    }
+}

+ 21 - 0
src/Renci.SshNet.TestTools.OpenSSH/Renci.SshNet.TestTools.OpenSSH.csproj

@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <TargetFramework>net7.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+
+        <!--
+          Even though we're not interested in producing XML docs for test projects, we have to enable this in order to have the .NET Compiler
+          Platform analyzers produce the IDE0005 (Remove unnecessary import) diagnostic.
+            
+          To avoid warnings for missing XML docs, we add CS1591 (Missing XML comment for publicly visible type or member) to the NoWarn property.
+
+          We can stop producing XML docs for test projects (and remove the NoWarn for CS1591) once the following issue is fixed:
+          https://github.com/dotnet/roslyn/issues/41640.
+      -->
+        <NoWarn>$(NoWarn);CS1591;SYSLIB0021;SYSLIB1045</NoWarn>
+    </PropertyGroup>
+    <ItemGroup>
+      <Folder Include="Properties\" />
+    </ItemGroup>
+</Project>

+ 501 - 0
src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs

@@ -0,0 +1,501 @@
+using System.Globalization;
+using System.Text;
+using System.Text.RegularExpressions;
+
+using Renci.SshNet.TestTools.OpenSSH.Formatters;
+
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+    public class SshdConfig
+    {
+        private static readonly Regex MatchRegex = new Regex($@"\s*Match\s+(User\s+(?<users>[\S]+))?\s*(Address\s+(?<addresses>[\S]+))?\s*", RegexOptions.Compiled);
+
+        private readonly SubsystemFormatter _subsystemFormatter;
+        private readonly Int32Formatter _int32Formatter;
+        private readonly BooleanFormatter _booleanFormatter;
+        private readonly MatchFormatter _matchFormatter;
+
+        private SshdConfig()
+        {
+            AcceptedEnvironmentVariables = new List<string>();
+            Ciphers = new List<Cipher>();
+            HostKeyFiles = new List<string>();
+            HostKeyAlgorithms = new List<HostKeyAlgorithm>();
+            KeyExchangeAlgorithms = new List<KeyExchangeAlgorithm>();
+            PublicKeyAcceptedAlgorithms = new List<PublicKeyAlgorithm>();
+            MessageAuthenticationCodeAlgorithms = new List<MessageAuthenticationCodeAlgorithm>();
+            Subsystems = new List<Subsystem>();
+            Matches = new List<Match>();
+            LogLevel = LogLevel.Info;
+            Port = 22;
+            Protocol = "2,1";
+
+            _booleanFormatter = new BooleanFormatter();
+            _int32Formatter = new Int32Formatter();
+            _matchFormatter = new MatchFormatter();
+            _subsystemFormatter = new SubsystemFormatter();
+        }
+
+        /// <summary>
+        /// Gets or sets the port number that sshd listens on.
+        /// </summary>
+        /// <value>
+        /// The port number that sshd listens on. The default is 22.
+        /// </value>
+        public int Port { get; set; }
+
+        /// <summary>
+        /// Gets or sets the list of private host key files used by sshd.
+        /// </summary>
+        /// <value>
+        /// A list of private host key files used by sshd.
+        /// </value>
+        public List<string> HostKeyFiles { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value specifying whether challenge-response authentication is allowed.
+        /// </summary>
+        /// <value>
+        /// A value specifying whether challenge-response authentication is allowed, or <see langword="null"/>
+        /// if this option is not configured.
+        /// </value>
+        public bool? ChallengeResponseAuthentication { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether to allow keyboard-interactive authentication.
+        /// </summary>
+        /// <value>
+        /// <see langword="true"/> to allow and <see langword="false"/> to disallow keyboard-interactive
+        /// authentication, or <see langword="null"/> if this option is not configured.
+        /// </value>
+        public bool? KeyboardInteractiveAuthentication { get; set; }
+
+        /// <summary>
+        /// Gets or sets the verbosity when logging messages from sshd.
+        /// </summary>
+        /// <value>
+        /// The verbosity when logging messages from sshd. The default is <see cref="LogLevel.Info"/>.
+        /// </value>
+        public LogLevel LogLevel { get; set; }
+
+        /// <summary>
+        /// Gets a sets a value indicating whether the Pluggable Authentication Module interface is enabled.
+        /// </summary>
+        /// <value>
+        /// A value indicating whether the Pluggable Authentication Module interface is enabled.
+        /// </value>
+        public bool? UsePAM { get; set; }
+
+        public List<Subsystem> Subsystems { get; }
+
+        /// <summary>
+        /// Gets a list of conditional blocks.
+        /// </summary>
+        public List<Match> Matches { get; }
+
+        public bool X11Forwarding { get; private set; }
+        public List<string> AcceptedEnvironmentVariables { get; private set; }
+        public List<Cipher> Ciphers { get; private set; }
+
+        /// <summary>
+        /// Gets the host key signature algorithms that the server offers.
+        /// </summary>
+        public List<HostKeyAlgorithm> HostKeyAlgorithms { get; private set; }
+
+        /// <summary>
+        /// Gets the available KEX (Key Exchange) algorithms.
+        /// </summary>
+        public List<KeyExchangeAlgorithm> KeyExchangeAlgorithms { get; private set; }
+
+        /// <summary>
+        /// Gets the signature algorithms that will be accepted for public key authentication.
+        /// </summary>
+        public List<PublicKeyAlgorithm> PublicKeyAcceptedAlgorithms { get; private set; }
+
+        /// <summary>
+        /// Gets the available MAC (message authentication code) algorithms.
+        /// </summary>
+        public List<MessageAuthenticationCodeAlgorithm> MessageAuthenticationCodeAlgorithms { get; private set; }
+
+        /// <summary>
+        /// Gets a value indicating whether <c>sshd</c> should print <c>/etc/motd</c> when a user logs in interactively.
+        /// </summary>
+        /// <value>
+        /// <see langword="true"/> if <c>sshd</c> should print <c>/etc/motd</c> when a user logs in interactively
+        /// and <see langword="false"/> if it should not; <see langword="null"/> if this option is not configured.
+        /// </value>
+        public bool? PrintMotd { get; set; }
+
+        /// <summary>
+        /// Gets or sets the protocol versions sshd supported.
+        /// </summary>
+        /// <value>
+        /// The protocol versions sshd supported. The default is <c>2,1</c>.
+        /// </value>
+        public string Protocol { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether TCP forwarding is allowed.
+        /// </summary>
+        /// <value>
+        /// <see langword="true"/> to allow and <see langword="false"/> to disallow TCP forwarding,
+        /// or <see langword="null"/> if this option is not configured.
+        /// </value>
+        public bool? AllowTcpForwarding { get; set; }
+
+        public void SaveTo(TextWriter writer)
+        {
+            writer.WriteLine("Protocol " + Protocol);
+            writer.WriteLine("Port " + _int32Formatter.Format(Port));
+            if (HostKeyFiles.Count > 0)
+            {
+                writer.WriteLine("HostKey " + string.Join(",", HostKeyFiles.ToArray()));
+            }
+
+            if (ChallengeResponseAuthentication is not null)
+            {
+                writer.WriteLine("ChallengeResponseAuthentication " + _booleanFormatter.Format(ChallengeResponseAuthentication.Value));
+            }
+
+            if (KeyboardInteractiveAuthentication is not null)
+            {
+                writer.WriteLine("KbdInteractiveAuthentication " + _booleanFormatter.Format(KeyboardInteractiveAuthentication.Value));
+            }
+
+            if (AllowTcpForwarding is not null)
+            {
+                writer.WriteLine("AllowTcpForwarding " + _booleanFormatter.Format(AllowTcpForwarding.Value));
+            }
+
+            if (PrintMotd is not null)
+            {
+                writer.WriteLine("PrintMotd " + _booleanFormatter.Format(PrintMotd.Value));
+            }
+
+            writer.WriteLine("LogLevel " + new LogLevelFormatter().Format(LogLevel));
+
+            foreach (var subsystem in Subsystems)
+            {
+                writer.WriteLine("Subsystem " + _subsystemFormatter.Format(subsystem));
+            }
+
+            if (UsePAM is not null)
+            {
+                writer.WriteLine("UsePAM " + _booleanFormatter.Format(UsePAM.Value));
+            }
+
+            writer.WriteLine("X11Forwarding " + _booleanFormatter.Format(X11Forwarding));
+
+            foreach (var acceptedEnvVar in AcceptedEnvironmentVariables)
+            {
+                writer.WriteLine("AcceptEnv " + acceptedEnvVar);
+            }
+
+            if (Ciphers.Count > 0)
+            {
+                writer.WriteLine("Ciphers " + string.Join(",", Ciphers.Select(c => c.Name).ToArray()));
+            }
+
+            if (HostKeyAlgorithms.Count > 0)
+            {
+                writer.WriteLine("HostKeyAlgorithms " + string.Join(",", HostKeyAlgorithms.Select(c => c.Name).ToArray()));
+            }
+
+            if (KeyExchangeAlgorithms.Count > 0)
+            {
+                writer.WriteLine("KexAlgorithms " + string.Join(",", KeyExchangeAlgorithms.Select(c => c.Name).ToArray()));
+            }
+
+            if (MessageAuthenticationCodeAlgorithms.Count > 0)
+            {
+                writer.WriteLine("MACs " + string.Join(",", MessageAuthenticationCodeAlgorithms.Select(c => c.Name).ToArray()));
+            }
+
+            writer.WriteLine("PubkeyAcceptedAlgorithms " + string.Join(",", PublicKeyAcceptedAlgorithms.Select(c => c.Name).ToArray()));
+
+            foreach (var match in Matches)
+            {
+                _matchFormatter.Format(match, writer);
+            }
+        }
+
+        public static SshdConfig LoadFrom(Stream stream, Encoding encoding)
+        {
+            using (var sr = new StreamReader(stream, encoding))
+            {
+                var sshdConfig = new SshdConfig();
+
+                Match? currentMatchConfiguration = null;
+
+                string? line;
+                while ((line = sr.ReadLine()) != null)
+                {
+                    // Skip empty lines
+                    if (line.Length == 0)
+                    {
+                        continue;
+                    }
+
+                    // Skip comments
+                    if (line[0] == '#')
+                    {
+                        continue;
+                    }
+
+                    var match = MatchRegex.Match(line);
+                    if (match.Success)
+                    {
+                        var usersGroup = match.Groups["users"];
+                        var addressesGroup = match.Groups["addresses"];
+                        var users = usersGroup.Success ? usersGroup.Value.Split(',') : Array.Empty<string>();
+                        var addresses = addressesGroup.Success ? addressesGroup.Value.Split(',') : Array.Empty<string>();
+
+                        currentMatchConfiguration = new Match(users, addresses);
+                        sshdConfig.Matches.Add(currentMatchConfiguration);
+                        continue;
+                    }
+
+                    if (currentMatchConfiguration != null)
+                    {
+                        ProcessMatchOption(currentMatchConfiguration, line);
+                    }
+                    else
+                    {
+                        ProcessGlobalOption(sshdConfig, line);
+                    }
+                }
+
+                if (sshdConfig.Ciphers == null)
+                {
+                    // Obtain supported ciphers using ssh -Q cipher
+                }
+
+                if (sshdConfig.KeyExchangeAlgorithms == null)
+                {
+                    // Obtain supports key exchange algorithms using ssh -Q kex
+                }
+
+                if (sshdConfig.HostKeyAlgorithms == null)
+                {
+                    // Obtain supports host key algorithms using ssh -Q key
+                }
+
+                if (sshdConfig.MessageAuthenticationCodeAlgorithms == null)
+                {
+                    // Obtain supported MACs using ssh -Q mac 
+                }
+
+
+                return sshdConfig;
+            }
+        }
+
+        private static void ProcessGlobalOption(SshdConfig sshdConfig, string line)
+        {
+            var matchOptionRegex = new Regex(@"^\s*(?<name>[\S]+)\s+(?<value>.+?){1}\s*$");
+
+            var optionsMatch = matchOptionRegex.Match(line);
+            if (!optionsMatch.Success)
+            {
+                return;
+            }
+
+            var nameGroup = optionsMatch.Groups["name"];
+            var valueGroup = optionsMatch.Groups["value"];
+
+            var name = nameGroup.Value;
+            var value = valueGroup.Value;
+
+            switch (name)
+            {
+                case "Port":
+                    sshdConfig.Port = ToInt(value);
+                    break;
+                case "HostKey":
+                    sshdConfig.HostKeyFiles = ParseCommaSeparatedValue(value);
+                    break;
+                case "ChallengeResponseAuthentication":
+                    sshdConfig.ChallengeResponseAuthentication = ToBool(value);
+                    break;
+                case "KbdInteractiveAuthentication":
+                    sshdConfig.KeyboardInteractiveAuthentication = ToBool(value);
+                    break;
+                case "LogLevel":
+                    sshdConfig.LogLevel = (LogLevel) Enum.Parse(typeof(LogLevel), value, true);
+                    break;
+                case "Subsystem":
+                    sshdConfig.Subsystems.Add(Subsystem.FromConfig(value));
+                    break;
+                case "UsePAM":
+                    sshdConfig.UsePAM = ToBool(value);
+                    break;
+                case "X11Forwarding":
+                    sshdConfig.X11Forwarding = ToBool(value);
+                    break;
+                case "Ciphers":
+                    sshdConfig.Ciphers = ParseCiphers(value);
+                    break;
+                case "KexAlgorithms":
+                    sshdConfig.KeyExchangeAlgorithms = ParseKeyExchangeAlgorithms(value);
+                    break;
+                case "PubkeyAcceptedAlgorithms":
+                    sshdConfig.PublicKeyAcceptedAlgorithms = ParsePublicKeyAcceptedAlgorithms(value);
+                    break;
+                case "HostKeyAlgorithms":
+                    sshdConfig.HostKeyAlgorithms = ParseHostKeyAlgorithms(value);
+                    break;
+                case "MACs":
+                    sshdConfig.MessageAuthenticationCodeAlgorithms = ParseMacs(value);
+                    break;
+                case "PrintMotd":
+                    sshdConfig.PrintMotd = ToBool(value);
+                    break;
+                case "AcceptEnv":
+                    ParseAcceptedEnvironmentVariable(sshdConfig, value);
+                    break;
+                case "Protocol":
+                    sshdConfig.Protocol = value;
+                    break;
+                case "AllowTcpForwarding":
+                    sshdConfig.AllowTcpForwarding = ToBool(value);
+                    break;
+                case "KeyRegenerationInterval":
+                case "HostbasedAuthentication":
+                case "ServerKeyBits":
+                case "SyslogFacility":
+                case "LoginGraceTime":
+                case "PermitRootLogin":
+                case "StrictModes":
+                case "RSAAuthentication":
+                case "PubkeyAuthentication":
+                case "IgnoreRhosts":
+                case "RhostsRSAAuthentication":
+                case "PermitEmptyPasswords":
+                case "X11DisplayOffset":
+                case "PrintLastLog":
+                case "TCPKeepAlive":
+                case "AuthorizedKeysFile":
+                case "PasswordAuthentication":
+                case "GatewayPorts":
+                    break;
+                default:
+                    throw new Exception($"Global option '{name}' is not implemented.");
+            }
+        }
+
+        private static void ParseAcceptedEnvironmentVariable(SshdConfig sshdConfig, string value)
+        {
+            var acceptedEnvironmentVariables = value.Split(' ');
+            foreach (var acceptedEnvironmentVariable in acceptedEnvironmentVariables)
+            {
+                sshdConfig.AcceptedEnvironmentVariables.Add(acceptedEnvironmentVariable);
+            }
+        }
+
+        private static List<Cipher> ParseCiphers(string value)
+        {
+            var cipherNames = value.Split(',');
+            var ciphers = new List<Cipher>(cipherNames.Length);
+            foreach (var cipherName in cipherNames)
+            {
+                ciphers.Add(new Cipher(cipherName.Trim()));
+            }
+            return ciphers;
+        }
+
+        private static List<KeyExchangeAlgorithm> ParseKeyExchangeAlgorithms(string value)
+        {
+            var kexNames = value.Split(',');
+            var keyExchangeAlgorithms = new List<KeyExchangeAlgorithm>(kexNames.Length);
+            foreach (var kexName in kexNames)
+            {
+                keyExchangeAlgorithms.Add(new KeyExchangeAlgorithm(kexName.Trim()));
+            }
+            return keyExchangeAlgorithms;
+        }
+
+        public static List<PublicKeyAlgorithm> ParsePublicKeyAcceptedAlgorithms(string value)
+        {
+            var publicKeyAlgorithmNames = value.Split(',');
+            var publicKeyAlgorithms = new List<PublicKeyAlgorithm>(publicKeyAlgorithmNames.Length);
+            foreach (var publicKeyAlgorithmName in publicKeyAlgorithmNames)
+            {
+                publicKeyAlgorithms.Add(new PublicKeyAlgorithm(publicKeyAlgorithmName.Trim()));
+            }
+            return publicKeyAlgorithms;
+        }
+
+        private static List<HostKeyAlgorithm> ParseHostKeyAlgorithms(string value)
+        {
+            var algorithmNames = value.Split(',');
+            var hostKeyAlgorithms = new List<HostKeyAlgorithm>(algorithmNames.Length);
+            foreach (var algorithmName in algorithmNames)
+            {
+                hostKeyAlgorithms.Add(new HostKeyAlgorithm(algorithmName.Trim()));
+            }
+            return hostKeyAlgorithms;
+        }
+
+        private static List<MessageAuthenticationCodeAlgorithm> ParseMacs(string value)
+        {
+            var macNames = value.Split(',');
+            var macAlgorithms = new List<MessageAuthenticationCodeAlgorithm>(macNames.Length);
+            foreach (var algorithmName in macNames)
+            {
+                macAlgorithms.Add(new MessageAuthenticationCodeAlgorithm(algorithmName.Trim()));
+            }
+            return macAlgorithms;
+        }
+
+        private static void ProcessMatchOption(Match matchConfiguration, string line)
+        {
+            var matchOptionRegex = new Regex(@"^\s+(?<name>[\S]+)\s+(?<value>.+?){1}\s*$");
+
+            var optionsMatch = matchOptionRegex.Match(line);
+            if (!optionsMatch.Success)
+            {
+                return;
+            }
+
+            var nameGroup = optionsMatch.Groups["name"];
+            var valueGroup = optionsMatch.Groups["value"];
+
+            var name = nameGroup.Value;
+            var value = valueGroup.Value;
+
+            switch (name)
+            {
+                case "AuthenticationMethods":
+                    matchConfiguration.AuthenticationMethods = value;
+                    break;
+                default:
+                    throw new Exception($"Match option '{name}' is not implemented.");
+            }
+        }
+
+
+        private static List<string> ParseCommaSeparatedValue(string value)
+        {
+            var values = value.Split(',');
+            return new List<string>(values);
+        }
+
+        private static bool ToBool(string value)
+        {
+            switch (value)
+            {
+                case "yes":
+                    return true;
+                case "no":
+                    return false;
+                default:
+                    throw new Exception($"Value '{value}' cannot be mapped to a boolean.");
+            }
+        }
+
+        private static int ToInt(string value)
+        {
+            return int.Parse(value, NumberFormatInfo.InvariantInfo);
+        }
+    }
+}

+ 41 - 0
src/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs

@@ -0,0 +1,41 @@
+using System.Text.RegularExpressions;
+
+namespace Renci.SshNet.TestTools.OpenSSH
+{
+    public class Subsystem
+    {
+        public Subsystem(string name, string command)
+        {
+            Name = name;
+            Command = command;
+        }
+
+        public string Name { get; }
+
+        public string Command { get; set; }
+
+        public void WriteTo(TextWriter writer)
+        {
+            writer.WriteLine(Name + "=" + Command);
+        }
+
+        public static Subsystem FromConfig(string value)
+        {
+            var subSystemValueRegex = new Regex(@"^\s*(?<name>[\S]+)\s+(?<command>.+?){1}\s*$");
+
+            var match = subSystemValueRegex.Match(value);
+            if (match.Success)
+            {
+                var nameGroup = match.Groups["name"];
+                var commandGroup = match.Groups["command"];
+
+                var name = nameGroup.Value;
+                var command = commandGroup.Value;
+
+                return new Subsystem(name, command);
+            }
+
+            throw new Exception($"'{value}' not recognized as value for Subsystem.");
+        }
+    }
+}

+ 1 - 1
src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs

@@ -36,7 +36,7 @@ namespace Renci.SshNet.Tests.Classes.Connection
         {
             Assert.IsNotNull(_actualException);
             Assert.IsNull(_actualException.InnerException);
-            Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode);
+            Assert.IsTrue(_actualException.SocketErrorCode is SocketError.HostNotFound or SocketError.TryAgain);
         }
     }
 }

+ 1 - 1
src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs

@@ -43,7 +43,7 @@ namespace Renci.SshNet.Tests.Classes.Connection
         {
             Assert.IsNotNull(_actualException);
             Assert.IsNull(_actualException.InnerException);
-            Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode);
+            Assert.IsTrue(_actualException.SocketErrorCode is SocketError.HostNotFound or SocketError.TryAgain);
         }
     }
 }

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingHttpContent.cs

@@ -12,7 +12,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
 
 using Renci.SshNet.Common;
-using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Connection

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_TimeoutReadingStatusLine.cs

@@ -10,7 +10,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
 
 using Renci.SshNet.Common;
-using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Connection

+ 1 - 1
src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_Comments.cs

@@ -1,5 +1,5 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
+
 using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 using System;

+ 1 - 1
src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_NoComments.cs

@@ -1,5 +1,5 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
+
 using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 using System;

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Connection/ProtocolVersionExchangeTest_ServerResponseValid_TerminatedByLineFeedWithoutCarriageReturn.cs

@@ -8,7 +8,6 @@ using System.Threading;
 
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
-using Renci.SshNet.Common;
 using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_ConnectionSucceeded.cs

@@ -10,7 +10,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
 
 using Renci.SshNet.Common;
-using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Connection

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingDestinationAddress.cs

@@ -10,7 +10,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
 
 using Renci.SshNet.Common;
-using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Connection

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyCode.cs

@@ -10,7 +10,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
 
 using Renci.SshNet.Common;
-using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Connection

+ 0 - 1
src/Renci.SshNet.Tests/Classes/Connection/Socks4ConnectorTest_Connect_TimeoutReadingReplyVersion.cs

@@ -1,7 +1,6 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
 using Renci.SshNet.Common;
-using Renci.SshNet.Connection;
 using Renci.SshNet.Tests.Common;
 using System;
 using System.Diagnostics;

+ 0 - 222
src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest.cs

@@ -1,16 +1,7 @@
 using System;
-using System.Diagnostics;
-#if NET6_0_OR_GREATER
-using System.Net.Http;
-#else
-using System.Net;
-#endif // NET6_0_OR_GREATER
-using System.Threading;
-using System.Threading.Tasks;
 
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
-using Renci.SshNet.Common;
 using Renci.SshNet.Tests.Common;
 using Renci.SshNet.Tests.Properties;
 
@@ -22,92 +13,6 @@ namespace Renci.SshNet.Tests.Classes
     [TestClass]
     public partial class ForwardedPortLocalTest : TestBase
     {
-        [TestMethod]
-        [WorkItem(713)]
-        [Owner("Kenneth_aa")]
-        [TestCategory("PortForwarding")]
-        [TestCategory("integration")]
-        [Description("Test if calling Stop on ForwardedPortLocal instance causes wait.")]
-        public void Test_PortForwarding_Local_Stop_Hangs_On_Wait()
-        {
-            using (var client = new SshClient(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD))
-            {
-                client.Connect();
-
-                var port1 = new ForwardedPortLocal("localhost", 8084, "www.google.com", 80);
-                client.AddForwardedPort(port1);
-                port1.Exception += delegate(object sender, ExceptionEventArgs e)
-                {
-                    Assert.Fail(e.Exception.ToString());
-                };
-
-                port1.Start();
-
-                var hasTestedTunnel = false;
-
-                _ = ThreadPool.QueueUserWorkItem(delegate(object state)
-                    {
-                        try
-                        {
-                            var url = "http://www.google.com/";
-                            Debug.WriteLine("Starting web request to \"" + url + "\"");
-
-#if NET6_0_OR_GREATER
-                            var httpClient = new HttpClient();
-                            var response = httpClient.GetAsync(url)
-                                                     .ConfigureAwait(false)
-                                                     .GetAwaiter()
-                                                     .GetResult();
-#else
-                            var request = (HttpWebRequest) WebRequest.Create(url);
-                            var response = (HttpWebResponse) request.GetResponse();
-#endif // NET6_0_OR_GREATER
-
-                            Assert.IsNotNull(response);
-
-                            Debug.WriteLine("Http Response status code: " + response.StatusCode.ToString());
-
-                            response.Dispose();
-
-                            hasTestedTunnel = true;
-                        }
-                        catch (Exception ex)
-                        {
-                            Assert.Fail(ex.ToString());
-                        }
-                    });
-
-                // Wait for the web request to complete.
-                while (!hasTestedTunnel)
-                {
-                    Thread.Sleep(1000);
-                }
-
-                try
-                {
-                    // Try stop the port forwarding, wait 3 seconds and fail if it is still started.
-                    _ = ThreadPool.QueueUserWorkItem(delegate(object state)
-                        {
-                            Debug.WriteLine("Trying to stop port forward.");
-                            port1.Stop();
-                            Debug.WriteLine("Port forwarding stopped.");
-                        });
-
-                    Thread.Sleep(3000);
-                    if (port1.IsStarted)
-                    {
-                        Assert.Fail("Port forwarding not stopped.");
-                    }
-                }
-                catch (Exception ex)
-                {
-                    Assert.Fail(ex.ToString());
-                }
-                client.Disconnect();
-                Debug.WriteLine("Success.");
-            }
-        }
-
         [TestMethod]
         public void ConstructorShouldThrowArgumentNullExceptionWhenBoundHostIsNull()
         {
@@ -177,132 +82,5 @@ namespace Renci.SshNet.Tests.Classes
 
             Assert.AreSame(host, forwardedPort.Host);
         }
-
-        /// <summary>
-        ///A test for ForwardedPortRemote Constructor
-        ///</summary>
-        [TestMethod]
-        [TestCategory("integration")]
-        public void Test_ForwardedPortRemote()
-        {
-            using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                #region Example SshClient AddForwardedPort Start Stop ForwardedPortLocal
-                client.Connect();
-                var port = new ForwardedPortLocal(8082, "www.cnn.com", 80);
-                client.AddForwardedPort(port);
-                port.Exception += delegate(object sender, ExceptionEventArgs e)
-                {
-                    Console.WriteLine(e.Exception.ToString());
-                };
-                port.Start();
-
-                Thread.Sleep(1000 * 60 * 20); //	Wait 20 minutes for port to be forwarded
-
-                port.Stop();
-                #endregion
-            }
-            Assert.Inconclusive("TODO: Implement code to verify target");
-        }
-
-        [TestMethod]
-        [TestCategory("integration")]
-        [ExpectedException(typeof(SshConnectionException))]
-        public void Test_PortForwarding_Local_Without_Connecting()
-        {
-            using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                var port1 = new ForwardedPortLocal("localhost", 8084, "www.renci.org", 80);
-                client.AddForwardedPort(port1);
-                port1.Exception += delegate (object sender, ExceptionEventArgs e)
-                    {
-                        Assert.Fail(e.Exception.ToString());
-                    };
-                port1.Start();
-
-                _ = Parallel.For(0,
-                                 100,
-                                 counter =>
-                                    {
-                                        var start = DateTime.Now;
-
-#if NET6_0_OR_GREATER
-                                        var httpClient = new HttpClient();
-                                        using (var response = httpClient.GetAsync("http://localhost:8084").GetAwaiter().GetResult())
-                                        {
-                                            var data = ReadStream(response.Content.ReadAsStream());
-#else
-                                        var request = (HttpWebRequest) WebRequest.Create("http://localhost:8084");
-                                        using (var response = (HttpWebResponse) request.GetResponse())
-                                        {
-                                            var data = ReadStream(response.GetResponseStream());
-#endif // NET6_0_OR_GREATER
-                                            var end = DateTime.Now;
-
-                                            Debug.WriteLine(string.Format("Request# {2}: Lenght: {0} Time: {1}", data.Length, end - start, counter));
-                                        }
-                                    });
-            }
-        }
-
-        [TestMethod]
-        [TestCategory("integration")]
-        public void Test_PortForwarding_Local()
-        {
-            using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                client.Connect();
-                var port1 = new ForwardedPortLocal("localhost", 8084, "www.renci.org", 80);
-                client.AddForwardedPort(port1);
-                port1.Exception += delegate (object sender, ExceptionEventArgs e)
-                    {
-                        Assert.Fail(e.Exception.ToString());
-                    };
-                port1.Start();
-
-                _ = Parallel.For(0,
-                                 100,
-                                 counter =>
-                                    {
-                                        var start = DateTime.Now;
-
-#if NET6_0_OR_GREATER
-                                        var httpClient = new HttpClient();
-                                        using (var response = httpClient.GetAsync("http://localhost:8084").GetAwaiter().GetResult())
-                                        {
-                                            var data = ReadStream(response.Content.ReadAsStream());
-#else
-                                        var request = (HttpWebRequest) WebRequest.Create("http://localhost:8084");
-                                        using (var response = (HttpWebResponse) request.GetResponse())
-                                        {
-                                            var data = ReadStream(response.GetResponseStream());
-#endif // NET6_0_OR_GREATER
-                                            var end = DateTime.Now;
-
-                                            Debug.WriteLine(string.Format("Request# {2}: Length: {0} Time: {1}", data.Length, end - start, counter));
-                                        }
-                                    });
-            }
-        }
-
-        private static byte[] ReadStream(System.IO.Stream stream)
-        {
-            var buffer = new byte[1024];
-            using (var ms = new System.IO.MemoryStream())
-            {
-                while (true)
-                {
-                    var read = stream.Read(buffer, 0, buffer.Length);
-                    if (read > 0)
-                    {
-                        ms.Write(buffer, 0, read);
-                    }
-                    else
-                    {
-                        return ms.ToArray();
-                    }
-                }
-            }
-        }
     }
 }

+ 0 - 103
src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest.cs

@@ -1,12 +1,8 @@
 using System;
-using System.Net;
-using System.Threading;
 
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
-using Renci.SshNet.Common;
 using Renci.SshNet.Tests.Common;
-using Renci.SshNet.Tests.Properties;
 
 namespace Renci.SshNet.Tests.Classes
 {
@@ -16,64 +12,6 @@ namespace Renci.SshNet.Tests.Classes
     [TestClass]
     public partial class ForwardedPortRemoteTest : TestBase
     {
-        [TestMethod]
-        [Description("Test passing null to AddForwardedPort hosts (remote).")]
-        [ExpectedException(typeof(ArgumentNullException))]
-        [TestCategory("integration")]
-        public void Test_AddForwardedPort_Remote_Hosts_Are_Null()
-        {
-            using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                client.Connect();
-                var port1 = new ForwardedPortRemote(boundHost: null, 8080, host: null, 80);
-                client.AddForwardedPort(port1);
-                client.Disconnect();
-            }
-        }
-
-        [TestMethod]
-        [Description("Test passing invalid port numbers to AddForwardedPort.")]
-        [ExpectedException(typeof(ArgumentOutOfRangeException))]
-        [TestCategory("integration")]
-        public void Test_AddForwardedPort_Invalid_PortNumber()
-        {
-            using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                client.Connect();
-                var port1 = new ForwardedPortRemote("localhost", IPEndPoint.MaxPort + 1, "www.renci.org", IPEndPoint.MaxPort + 1);
-                client.AddForwardedPort(port1);
-                client.Disconnect();
-            }
-        }
-
-        /// <summary>
-        ///A test for ForwardedPortRemote Constructor
-        ///</summary>
-        [TestMethod]
-        [TestCategory("integration")]
-        public void Test_ForwardedPortRemote()
-        {
-            using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                #region Example SshClient AddForwardedPort Start Stop ForwardedPortRemote
-                client.Connect();
-                var port = new ForwardedPortRemote(8082, "www.cnn.com", 80);
-                client.AddForwardedPort(port);
-                port.Exception += delegate(object sender, ExceptionEventArgs e)
-                {
-                    Console.WriteLine(e.Exception.ToString());
-                };
-                port.Start();
-
-                Thread.Sleep(1000 * 60 * 20); //	Wait 20 minutes for port to be forwarded
-
-                port.Stop();
-                #endregion
-            }
-            Assert.Inconclusive("TODO: Implement code to verify target");
-        }
-
-
         /// <summary>
         ///A test for Stop
         ///</summary>
@@ -152,46 +90,5 @@ namespace Renci.SshNet.Tests.Classes
             var target = new ForwardedPortRemote(boundPort, host, port);
             Assert.Inconclusive("TODO: Implement code to verify target");
         }
-
-#if FEATURE_TPL
-        [TestMethod]
-        [TestCategory("integration")]
-        public void Test_PortForwarding_Remote()
-        {
-            //  ******************************************************************
-            //  ************* Tests are still in not finished ********************
-            //  ******************************************************************
-
-            using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                client.Connect();
-                var port1 = new ForwardedPortRemote(8082, "www.renci.org", 80);
-                client.AddForwardedPort(port1);
-                port1.Exception += delegate (object sender, ExceptionEventArgs e)
-                {
-                    Assert.Fail(e.Exception.ToString());
-                };
-                port1.Start();
-                var boundport = port1.BoundPort;
-
-                System.Threading.Tasks.Parallel.For(0, 5,
-
-                    //new ParallelOptions
-                    //{
-                    //    MaxDegreeOfParallelism = 1,
-                    //},
-                    (counter) =>
-                    {
-                        var cmd = client.CreateCommand(string.Format("wget -O- http://localhost:{0}", boundport));
-                        var result = cmd.Execute();
-                        var end = DateTime.Now;
-                        System.Diagnostics.Debug.WriteLine(string.Format("Length: {0}", result.Length));
-                    }
-                );
-                Thread.Sleep(1000 * 100);
-                port1.Stop();
-            }
-        }
-#endif // FEATURE_TPL
     }
 }

+ 2 - 38
src/Renci.SshNet.Tests/Classes/KeyboardInteractiveConnectionInfoTest.cs

@@ -1,8 +1,6 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
+
 using Renci.SshNet.Tests.Common;
-using Renci.SshNet.Tests.Properties;
-using System;
 
 namespace Renci.SshNet.Tests.Classes
 {
@@ -12,40 +10,6 @@ namespace Renci.SshNet.Tests.Classes
     [TestClass]
     public class KeyboardInteractiveConnectionInfoTest : TestBase
     {
-        [TestMethod]
-        [TestCategory("KeyboardInteractiveConnectionInfo")]
-        [TestCategory("integration")]
-        public void Test_KeyboardInteractiveConnectionInfo()
-        {
-            var host = Resources.HOST;
-            var username = Resources.USERNAME;
-            var password = Resources.PASSWORD;
-
-            #region Example KeyboardInteractiveConnectionInfo AuthenticationPrompt
-            var connectionInfo = new KeyboardInteractiveConnectionInfo(host, username);
-            connectionInfo.AuthenticationPrompt += delegate(object sender, AuthenticationPromptEventArgs e)
-            {
-                System.Console.WriteLine(e.Instruction);
-
-                foreach (var prompt in e.Prompts)
-                {
-                    Console.WriteLine(prompt.Request);
-                    prompt.Response = Console.ReadLine();
-                }
-            };
-
-            using (var client = new SftpClient(connectionInfo))
-            {
-                client.Connect();
-                //  Do something here
-                client.Disconnect();
-            }
-            #endregion
-
-            Assert.AreEqual(connectionInfo.Host, Resources.HOST);
-            Assert.AreEqual(connectionInfo.Username, Resources.USERNAME);
-        }
-
         /// <summary>
         ///A test for Dispose
         ///</summary>
@@ -192,4 +156,4 @@ namespace Renci.SshNet.Tests.Classes
             Assert.Inconclusive("TODO: Implement code to verify target");
         }
     }
-}
+}

+ 1 - 28
src/Renci.SshNet.Tests/Classes/PasswordAuthenticationMethodTest.cs

@@ -1,6 +1,5 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Renci.SshNet.Tests.Common;
-using Renci.SshNet.Tests.Properties;
 using System;
 
 namespace Renci.SshNet.Tests.Classes
@@ -59,32 +58,6 @@ namespace Renci.SshNet.Tests.Classes
             new PasswordAuthenticationMethod("valid", string.Empty);
         }
 
-        [TestMethod]
-        [WorkItem(1140)]
-        [TestCategory("BaseClient")]
-        [TestCategory("integration")]
-        [Description("Test whether IsConnected is false after disconnect.")]
-        [Owner("Kenneth_aa")]
-        public void Test_BaseClient_IsConnected_True_After_Disconnect()
-        {
-            // 2012-04-29 - Kenneth_aa
-            // The problem with this test, is that after SSH Net calls .Disconnect(), the library doesn't wait
-            // for the server to confirm disconnect before IsConnected is checked. And now I'm not mentioning
-            // anything about Socket's either.
-
-            var connectionInfo = new PasswordAuthenticationMethod(Resources.USERNAME, Resources.PASSWORD);
-
-            using (SftpClient client = new SftpClient(Resources.HOST, int.Parse(Resources.PORT), Resources.USERNAME, Resources.PASSWORD))
-            {
-                client.Connect();
-                Assert.AreEqual<bool>(true, client.IsConnected, "IsConnected is not true after Connect() was called.");
-
-                client.Disconnect();
-
-                Assert.AreEqual<bool>(false, client.IsConnected, "IsConnected is true after Disconnect() was called.");
-            }
-        }
-
         /// <summary>
         ///A test for Name
         ///</summary>
@@ -158,4 +131,4 @@ namespace Renci.SshNet.Tests.Classes
             Assert.Inconclusive("TODO: Implement code to verify target");
         }
     }
-}
+}

+ 1 - 133
src/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs

@@ -1,5 +1,4 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Renci.SshNet.Common;
 using Renci.SshNet.Tests.Common;
 using Renci.SshNet.Tests.Properties;
 using System;
@@ -13,84 +12,6 @@ namespace Renci.SshNet.Tests.Classes
     [TestClass]
     public class PasswordConnectionInfoTest : TestBase
     {
-        [TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
-        [TestCategory("integration")]
-        public void Test_PasswordConnectionInfo()
-        {
-            var host = Resources.HOST;
-            var username = Resources.USERNAME;
-            var password = Resources.PASSWORD;
-
-            #region Example PasswordConnectionInfo
-            var connectionInfo = new PasswordConnectionInfo(host, username, password);
-            using (var client = new SftpClient(connectionInfo))
-            {
-                client.Connect();
-                //  Do something here
-                client.Disconnect();
-            }
-            #endregion
-
-            Assert.AreEqual(connectionInfo.Host, Resources.HOST);
-            Assert.AreEqual(connectionInfo.Username, Resources.USERNAME);
-        }
-
-        [TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
-        [TestCategory("integration")]
-        public void Test_PasswordConnectionInfo_PasswordExpired()
-        {
-            var host = Resources.HOST;
-            var username = Resources.USERNAME;
-            var password = Resources.PASSWORD;
-
-            #region Example PasswordConnectionInfo PasswordExpired
-            var connectionInfo = new PasswordConnectionInfo("host", "username", "password");
-            var encoding = SshData.Ascii;
-            connectionInfo.PasswordExpired += delegate(object sender, AuthenticationPasswordChangeEventArgs e)
-            {
-                e.NewPassword = encoding.GetBytes("123456");
-            };
-
-            using (var client = new SshClient(connectionInfo))
-            {
-                client.Connect();
-
-                client.Disconnect();
-            }
-            #endregion
-
-            Assert.Inconclusive();
-        }
-        [TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
-        [TestCategory("integration")]
-        public void Test_PasswordConnectionInfo_AuthenticationBanner()
-        {
-            var host = Resources.HOST;
-            var username = Resources.USERNAME;
-            var password = Resources.PASSWORD;
-
-            #region Example PasswordConnectionInfo AuthenticationBanner
-            var connectionInfo = new PasswordConnectionInfo(host, username, password);
-            connectionInfo.AuthenticationBanner += delegate(object sender, AuthenticationBannerEventArgs e)
-            {
-                Console.WriteLine(e.BannerMessage);
-            };
-            using (var client = new SftpClient(connectionInfo))
-            {
-                client.Connect();
-                //  Do something here
-                client.Disconnect();
-            }
-            #endregion
-
-            Assert.AreEqual(connectionInfo.Host, Resources.HOST);
-            Assert.AreEqual(connectionInfo.Username, Resources.USERNAME);
-        }
-
-
         [WorkItem(703), TestMethod]
         [TestCategory("PasswordConnectionInfo")]
         public void Test_ConnectionInfo_Host_Is_Null()
@@ -148,60 +69,7 @@ namespace Renci.SshNet.Tests.Classes
         {
             _ = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MaxPort + 1, Resources.USERNAME, Resources.PASSWORD);
         }
-
-        [TestMethod]
-        [Owner("Kenneth_aa")]
-        [Description("Test connect to remote server via a SOCKS4 proxy server.")]
-        [TestCategory("Proxy")]
-        [TestCategory("integration")]
-        public void Test_Ssh_Connect_Via_Socks4()
-        {
-            var connInfo = new PasswordConnectionInfo(Resources.HOST, Resources.USERNAME, Resources.PASSWORD, ProxyTypes.Socks4, Resources.PROXY_HOST, int.Parse(Resources.PROXY_PORT));
-            using (var client = new SshClient(connInfo))
-            {
-                client.Connect();
-
-                var ret = client.RunCommand("ls -la");
-
-                client.Disconnect();
-            }
-        }
-
-        [TestMethod]
-        [Owner("Kenneth_aa")]
-        [Description("Test connect to remote server via a TCP SOCKS5 proxy server.")]
-        [TestCategory("Proxy")]
-        [TestCategory("integration")]
-        public void Test_Ssh_Connect_Via_TcpSocks5()
-        {
-            var connInfo = new PasswordConnectionInfo(Resources.HOST, Resources.USERNAME, Resources.PASSWORD, ProxyTypes.Socks5, Resources.PROXY_HOST, int.Parse(Resources.PROXY_PORT));
-            using (var client = new SshClient(connInfo))
-            {
-                client.Connect();
-
-                var ret = client.RunCommand("ls -la");
-                client.Disconnect();
-            }
-        }
-
-        [TestMethod]
-        [Owner("Kenneth_aa")]
-        [Description("Test connect to remote server via a HTTP proxy server.")]
-        [TestCategory("Proxy")]
-        [TestCategory("integration")]
-        public void Test_Ssh_Connect_Via_HttpProxy()
-        {
-            var connInfo = new PasswordConnectionInfo(Resources.HOST, Resources.USERNAME, Resources.PASSWORD, ProxyTypes.Http, Resources.PROXY_HOST, int.Parse(Resources.PROXY_PORT));
-            using (var client = new SshClient(connInfo))
-            {
-                client.Connect();
-
-                var ret = client.RunCommand("ls -la");
-
-                client.Disconnect();
-            }
-        }
-
+        
         /// <summary>
         ///A test for Dispose
         ///</summary>

+ 1 - 51
src/Renci.SshNet.Tests/Classes/PrivateKeyConnectionInfoTest.cs

@@ -1,8 +1,5 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Renci.SshNet.Tests.Common;
-using Renci.SshNet.Tests.Properties;
-using System.IO;
-using System.Text;
 
 namespace Renci.SshNet.Tests.Classes
 {
@@ -12,53 +9,6 @@ namespace Renci.SshNet.Tests.Classes
     [TestClass]
     public class PrivateKeyConnectionInfoTest : TestBase
     {
-        [TestMethod]
-        [TestCategory("PrivateKeyConnectionInfo")]
-        [TestCategory("integration")]
-        public void Test_PrivateKeyConnectionInfo()
-        {
-            var host = Resources.HOST;
-            var username = Resources.USERNAME;
-            MemoryStream keyFileStream = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITHOUT_PASS));
-
-            #region Example PrivateKeyConnectionInfo PrivateKeyFile
-            var connectionInfo = new PrivateKeyConnectionInfo(host, username, new PrivateKeyFile(keyFileStream));
-            using (var client = new SshClient(connectionInfo))
-            {
-                client.Connect();
-                client.Disconnect();
-            }
-            #endregion
-
-            Assert.AreEqual(connectionInfo.Host, Resources.HOST);
-            Assert.AreEqual(connectionInfo.Username, Resources.USERNAME);
-        }
-
-        [TestMethod]
-        [TestCategory("PrivateKeyConnectionInfo")]
-        [TestCategory("integration")]
-        public void Test_PrivateKeyConnectionInfo_MultiplePrivateKey()
-        {
-            var host = Resources.HOST;
-            var username = Resources.USERNAME;
-            MemoryStream keyFileStream1 = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITHOUT_PASS));
-            MemoryStream keyFileStream2 = new MemoryStream(Encoding.ASCII.GetBytes(Resources.RSA_KEY_WITHOUT_PASS));
-
-            #region Example PrivateKeyConnectionInfo PrivateKeyFile Multiple
-            var connectionInfo = new PrivateKeyConnectionInfo(host, username, 
-                new PrivateKeyFile(keyFileStream1), 
-                new PrivateKeyFile(keyFileStream2));
-            using (var client = new SshClient(connectionInfo))
-            {
-                client.Connect();
-                client.Disconnect();
-            }
-            #endregion
-
-            Assert.AreEqual(connectionInfo.Host, Resources.HOST);
-            Assert.AreEqual(connectionInfo.Username, Resources.USERNAME);
-        }
-
         /// <summary>
         ///A test for Dispose
         ///</summary>
@@ -214,4 +164,4 @@ namespace Renci.SshNet.Tests.Classes
             Assert.Inconclusive("TODO: Implement code to verify target");
         }
     }
-}
+}

+ 0 - 328
src/Renci.SshNet.Tests/Classes/ScpClientTest.cs

@@ -1,15 +1,11 @@
 using System;
 using System.IO;
-using System.Linq;
-using System.Security.Cryptography;
 using System.Text;
-using System.Threading.Tasks;
 
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
 using Renci.SshNet.Common;
 using Renci.SshNet.Tests.Common;
-using Renci.SshNet.Tests.Properties;
 
 namespace Renci.SshNet.Tests.Classes
 {
@@ -219,203 +215,6 @@ namespace Renci.SshNet.Tests.Classes
             Assert.AreSame(RemotePathTransformation.ShellQuote, client.RemotePathTransformation);
         }
 
-        [TestMethod]
-        [TestCategory("Scp")]
-        [TestCategory("integration")]
-        public void Test_Scp_File_Upload_Download()
-        {
-            RemoveAllFiles();
-
-            using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                scp.Connect();
-
-                var uploadedFileName = Path.GetTempFileName();
-                var downloadedFileName = Path.GetTempFileName();
-
-                CreateTestFile(uploadedFileName, 1);
-
-                scp.Upload(new FileInfo(uploadedFileName), Path.GetFileName(uploadedFileName));
-
-                scp.Download(Path.GetFileName(uploadedFileName), new FileInfo(downloadedFileName));
-
-                //  Calculate MD5 value
-                var uploadedHash = CalculateMD5(uploadedFileName);
-                var downloadedHash = CalculateMD5(downloadedFileName);
-
-                File.Delete(uploadedFileName);
-                File.Delete(downloadedFileName);
-
-                scp.Disconnect();
-
-                Assert.AreEqual(uploadedHash, downloadedHash);
-            }
-        }
-
-        [TestMethod]
-        [TestCategory("Scp")]
-        [TestCategory("integration")]
-        public void Test_Scp_Stream_Upload_Download()
-        {
-            RemoveAllFiles();
-
-            using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                scp.Connect();
-
-                var uploadedFileName = Path.GetTempFileName();
-                var downloadedFileName = Path.GetTempFileName();
-
-                CreateTestFile(uploadedFileName, 1);
-
-                //  Calculate has value
-                using (var stream = File.OpenRead(uploadedFileName))
-                {
-                    scp.Upload(stream, Path.GetFileName(uploadedFileName));
-                }
-
-                using (var stream = File.OpenWrite(downloadedFileName))
-                {
-                    scp.Download(Path.GetFileName(uploadedFileName), stream);
-                }
-
-                //  Calculate MD5 value
-                var uploadedHash = CalculateMD5(uploadedFileName);
-                var downloadedHash = CalculateMD5(downloadedFileName);
-
-                File.Delete(uploadedFileName);
-                File.Delete(downloadedFileName);
-
-                scp.Disconnect();
-
-                Assert.AreEqual(uploadedHash, downloadedHash);
-            }
-        }
-
-        [TestMethod]
-        [TestCategory("Scp")]
-        [TestCategory("integration")]
-        public void Test_Scp_10MB_File_Upload_Download()
-        {
-            RemoveAllFiles();
-
-            using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                scp.Connect();
-
-                var uploadedFileName = Path.GetTempFileName();
-                var downloadedFileName = Path.GetTempFileName();
-
-                CreateTestFile(uploadedFileName, 10);
-
-                scp.Upload(new FileInfo(uploadedFileName), Path.GetFileName(uploadedFileName));
-
-                scp.Download(Path.GetFileName(uploadedFileName), new FileInfo(downloadedFileName));
-
-                //  Calculate MD5 value
-                var uploadedHash = CalculateMD5(uploadedFileName);
-                var downloadedHash = CalculateMD5(downloadedFileName);
-
-                File.Delete(uploadedFileName);
-                File.Delete(downloadedFileName);
-
-                scp.Disconnect();
-
-                Assert.AreEqual(uploadedHash, downloadedHash);
-            }
-        }
-
-        [TestMethod]
-        [TestCategory("Scp")]
-        [TestCategory("integration")]
-        public void Test_Scp_10MB_Stream_Upload_Download()
-        {
-            RemoveAllFiles();
-
-            using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                scp.Connect();
-
-                var uploadedFileName = Path.GetTempFileName();
-                var downloadedFileName = Path.GetTempFileName();
-
-                CreateTestFile(uploadedFileName, 10);
-
-                //  Calculate has value
-                using (var stream = File.OpenRead(uploadedFileName))
-                {
-                    scp.Upload(stream, Path.GetFileName(uploadedFileName));
-                }
-
-                using (var stream = File.OpenWrite(downloadedFileName))
-                {
-                    scp.Download(Path.GetFileName(uploadedFileName), stream);
-                }
-
-                //  Calculate MD5 value
-                var uploadedHash = CalculateMD5(uploadedFileName);
-                var downloadedHash = CalculateMD5(downloadedFileName);
-
-                File.Delete(uploadedFileName);
-                File.Delete(downloadedFileName);
-
-                scp.Disconnect();
-
-                Assert.AreEqual(uploadedHash, downloadedHash);
-            }
-        }
-
-        [TestMethod]
-        [TestCategory("Scp")]
-        [TestCategory("integration")]
-        public void Test_Scp_Directory_Upload_Download()
-        {
-            RemoveAllFiles();
-
-            using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                scp.Connect();
-
-                var uploadDirectory =
-                    Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName()));
-                for (var i = 0; i < 3; i++)
-                {
-                    var subfolder = Directory.CreateDirectory(string.Format(@"{0}\folder_{1}", uploadDirectory.FullName, i));
-
-                    for (var j = 0; j < 5; j++)
-                    {
-                        CreateTestFile(string.Format(@"{0}\file_{1}", subfolder.FullName, j), 1);
-                    }
-
-                    CreateTestFile(string.Format(@"{0}\file_{1}", uploadDirectory.FullName, i), 1);
-                }
-
-                scp.Upload(uploadDirectory, "uploaded_dir");
-
-                var downloadDirectory =
-                    Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName()));
-
-                scp.Download("uploaded_dir", downloadDirectory);
-
-                var uploadedFiles = uploadDirectory.GetFiles("*.*", SearchOption.AllDirectories);
-                var downloadFiles = downloadDirectory.GetFiles("*.*", SearchOption.AllDirectories);
-
-                var result = from f1 in uploadedFiles
-                    from f2 in downloadFiles
-                    where
-                    f1.FullName.Substring(uploadDirectory.FullName.Length) ==
-                    f2.FullName.Substring(downloadDirectory.FullName.Length)
-                    && CalculateMD5(f1.FullName) == CalculateMD5(f2.FullName)
-                    select f1;
-
-                var counter = result.Count();
-
-                scp.Disconnect();
-
-                Assert.IsTrue(counter == uploadedFiles.Length && uploadedFiles.Length == downloadFiles.Length);
-            }
-        }
-
         /// <summary>
         ///A test for OperationTimeout
         ///</summary>
@@ -539,133 +338,6 @@ namespace Renci.SshNet.Tests.Classes
             Assert.Inconclusive("A method that does not return a value cannot be verified.");
         }
 
-        [TestMethod]
-        [TestCategory("Scp")]
-        [TestCategory("integration")]
-        public void Test_Scp_File_20_Parallel_Upload_Download()
-        {
-            using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                scp.Connect();
-
-                var uploadFilenames = new string[20];
-                for (var i = 0; i < uploadFilenames.Length; i++)
-                {
-                    uploadFilenames[i] = Path.GetTempFileName();
-                    CreateTestFile(uploadFilenames[i], 1);
-                }
-
-                _ = Parallel.ForEach(uploadFilenames,
-                                     filename =>
-                                        {
-                                            scp.Upload(new FileInfo(filename), Path.GetFileName(filename));
-                                        });
-                _ = Parallel.ForEach(uploadFilenames,
-                                     filename =>
-                                        {
-                                            scp.Download(Path.GetFileName(filename), new FileInfo(string.Format("{0}.down", filename)));
-                                        });
-
-                var result = from file in uploadFilenames
-                             where CalculateMD5(file) == CalculateMD5(string.Format("{0}.down", file))
-                             select file;
-
-                scp.Disconnect();
-
-                Assert.IsTrue(result.Count() == uploadFilenames.Length);
-            }
-        }
-
-        [TestMethod]
-        [TestCategory("Scp")]
-        [TestCategory("integration")]
-        public void Test_Scp_File_Upload_Download_Events()
-        {
-            using (var scp = new ScpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                scp.Connect();
-
-                var uploadFilenames = new string[10];
-
-                for (var i = 0; i < uploadFilenames.Length; i++)
-                {
-                    uploadFilenames[i] = Path.GetTempFileName();
-                    CreateTestFile(uploadFilenames[i], 1);
-                }
-
-                var uploadedFiles = uploadFilenames.ToDictionary(Path.GetFileName, (filename) => 0L);
-                var downloadedFiles = uploadFilenames.ToDictionary((filename) => string.Format("{0}.down", Path.GetFileName(filename)), (filename) => 0L);
-
-                scp.Uploading += delegate (object sender, ScpUploadEventArgs e)
-                {
-                    uploadedFiles[e.Filename] = e.Uploaded;
-                };
-
-                scp.Downloading += delegate (object sender, ScpDownloadEventArgs e)
-                {
-                    downloadedFiles[string.Format("{0}.down", e.Filename)] = e.Downloaded;
-                };
-
-                _ = Parallel.ForEach(uploadFilenames,
-                                     filename =>
-                                        {
-                                            scp.Upload(new FileInfo(filename), Path.GetFileName(filename));
-                                        });
-                _ = Parallel.ForEach(uploadFilenames,
-                                     filename =>
-                                        {
-                                            scp.Download(Path.GetFileName(filename), new FileInfo(string.Format("{0}.down", filename)));
-                                        });
-
-                var result = from uf in uploadedFiles
-                             from df in downloadedFiles
-                             where string.Format("{0}.down", uf.Key) == df.Key && uf.Value == df.Value
-                             select uf;
-
-                scp.Disconnect();
-
-                Assert.IsTrue(result.Count() == uploadFilenames.Length && uploadFilenames.Length == uploadedFiles.Count && uploadedFiles.Count == downloadedFiles.Count);
-            }
-        }
-
-        protected static string CalculateMD5(string fileName)
-        {
-            using (var file = new FileStream(fileName, FileMode.Open))
-            {
-#if NET7_0_OR_GREATER
-                var hash = MD5.HashData(file);
-#else
-#if NET6_0
-                var md5 = MD5.Create();
-#else
-                MD5 md5 = new MD5CryptoServiceProvider();
-#endif // NET6_0
-                var hash = md5.ComputeHash(file);
-#endif // NET7_0_OR_GREATER
-
-                file.Close();
-
-                var sb = new StringBuilder();
-
-                for (var i = 0; i < hash.Length; i++)
-                {
-                    _ = sb.Append(i.ToString("x2"));
-                }
-
-                return sb.ToString();
-            }
-        }
-
-        private static void RemoveAllFiles()
-        {
-            using (var client = new SshClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
-            {
-                client.Connect();
-                _ = client.RunCommand("rm -rf *");
-                client.Disconnect();
-            }
-        }
-
         private PrivateKeyFile GetRsaKey()
         {
             using (var stream = GetData("Key.RSA.txt"))

Някои файлове не бяха показани, защото твърде много файлове са промени