diff --git a/src/Renci.SshNet.IntegrationTests/.gitignore b/src/Renci.SshNet.IntegrationTests/.gitignore
new file mode 100644
index 000000000..0819dad73
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/.gitignore
@@ -0,0 +1 @@
+TestResults/
\ No newline at end of file
diff --git a/src/Renci.SshNet.IntegrationTests/App.config b/src/Renci.SshNet.IntegrationTests/App.config
new file mode 100644
index 000000000..c9794e84d
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/App.config
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs b/src/Renci.SshNet.IntegrationTests/AuthenticationMethodFactory.cs
new file mode 100644
index 000000000..638b285d8
--- /dev/null
+++ b/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;
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/AuthenticationTests.cs b/src/Renci.SshNet.IntegrationTests/AuthenticationTests.cs
new file mode 100644
index 000000000..c3968e949
--- /dev/null
+++ b/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);
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/Common/ArrayBuilder.cs b/src/Renci.SshNet.IntegrationTests/Common/ArrayBuilder.cs
new file mode 100644
index 000000000..1720c19c9
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/Common/ArrayBuilder.cs
@@ -0,0 +1,32 @@
+namespace Renci.SshNet.IntegrationTests.Common
+{
+ public class ArrayBuilder
+ {
+ private readonly List _buffer;
+
+ public ArrayBuilder()
+ {
+ _buffer = new List();
+ }
+
+ public ArrayBuilder Add(T[] array)
+ {
+ return Add(array, 0, array.Length);
+ }
+
+ public ArrayBuilder 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();
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/Common/AsyncSocketListener.cs b/src/Renci.SshNet.IntegrationTests/Common/AsyncSocketListener.cs
new file mode 100644
index 000000000..753385582
--- /dev/null
+++ b/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 _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();
+ _syncLock = new object();
+ ShutdownRemoteCommunicationSocket = true;
+ }
+
+ ///
+ /// Gets a value indicating whether the is invoked on the
+ /// that is used to handle the communication with the remote host, when the remote host has closed the connection.
+ ///
+ ///
+ /// to invoke on the that is used
+ /// to handle the communication with the remote host, when the remote host has closed the connection; otherwise,
+ /// . The default is .
+ ///
+ 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;
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs b/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs
new file mode 100644
index 000000000..64676a0d2
--- /dev/null
+++ b/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();
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs b/src/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs
new file mode 100644
index 000000000..e50858c33
--- /dev/null
+++ b/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;
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/ConnectivityTests.cs b/src/Renci.SshNet.IntegrationTests/ConnectivityTests.cs
new file mode 100644
index 000000000..5401e6b6b
--- /dev/null
+++ b/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);
+ }
+
+ ///
+ /// Verifies whether we handle a disconnect initiated by the SSH server (through a SSH_MSG_DISCONNECT message).
+ ///
+ ///
+ /// We force this by only configuring keyboard-interactive as authentication method, while ChallengeResponseAuthentication
+ /// is not enabled. This causes OpenSSH to terminate the connection because there are no authentication methods left.
+ ///
+ [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);
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/Credential.cs b/src/Renci.SshNet.IntegrationTests/Credential.cs
new file mode 100644
index 000000000..ee62d0f7b
--- /dev/null
+++ b/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; }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/HostConfig.cs b/src/Renci.SshNet.IntegrationTests/HostConfig.cs
new file mode 100644
index 000000000..817bd7026
--- /dev/null
+++ b/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(@"^(?[\S]+)\s+(?[a-zA-Z]+[a-zA-Z\-\.]*[a-zA-Z]+)\s*(?.+)*$", RegexOptions.Singleline);
+
+ public List Entries { get; }
+
+ private HostConfig()
+ {
+ Entries = new List();
+ }
+
+ 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();
+ }
+
+ public IPAddress IPAddress { get; private set; }
+ public string HostName { get; set; }
+ public List Aliases { get; }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs b/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs
new file mode 100644
index 000000000..3732f1e22
--- /dev/null
+++ b/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();
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/HostKeyFile.cs b/src/Renci.SshNet.IntegrationTests/HostKeyFile.cs
new file mode 100644
index 000000000..01cf957f5
--- /dev/null
+++ b/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; }
+ }
+
+
+}
diff --git a/src/Renci.SshNet.IntegrationTests/IConnectionInfoFactory.cs b/src/Renci.SshNet.IntegrationTests/IConnectionInfoFactory.cs
new file mode 100644
index 000000000..858dd0872
--- /dev/null
+++ b/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);
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs b/src/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs
new file mode 100644
index 000000000..dfd6b6394
--- /dev/null
+++ b/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();
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/LinuxAdminConnectionFactory.cs b/src/Renci.SshNet.IntegrationTests/LinuxAdminConnectionFactory.cs
new file mode 100644
index 000000000..dfb5c1369
--- /dev/null
+++ b/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();
+ }
+ }
+}
+
diff --git a/src/Renci.SshNet.IntegrationTests/LinuxVMConnectionFactory.cs b/src/Renci.SshNet.IntegrationTests/LinuxVMConnectionFactory.cs
new file mode 100644
index 000000000..f2f04dbfc
--- /dev/null
+++ b/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);
+ }
+ }
+}
+
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/AesCipherTests.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/AesCipherTests.cs
new file mode 100644
index 000000000..7e9e97b01
--- /dev/null
+++ b/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();
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ForwardedPortLocalTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ForwardedPortLocalTest.cs
new file mode 100644
index 000000000..bf6292faa
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ForwardedPortLocalTest.cs
@@ -0,0 +1,158 @@
+using System.Diagnostics;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+ ///
+ /// Provides functionality for local port forwarding
+ ///
+ [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();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/HMacTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/HMacTest.cs
new file mode 100644
index 000000000..c8df34e9d
--- /dev/null
+++ b/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();
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ScpClientTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ScpClientTest.cs
new file mode 100644
index 000000000..e9015a3c6
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/ScpClientTest.cs
@@ -0,0 +1,336 @@
+using System.Security.Cryptography;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+ ///
+ /// Provides SCP client functionality.
+ ///
+ [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();
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ChangeDirectory.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ChangeDirectory.cs
similarity index 66%
rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.ChangeDirectory.cs
rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ChangeDirectory.cs
index 5c58e1fa1..6fb518506 100644
--- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ChangeDirectory.cs
+++ b/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
{
///
/// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
///
- 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 @@ public void Test_Sftp_ChangeDirectory_Root_Dont_Exists()
[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 @@ public void Test_Sftp_ChangeDirectory_Root_With_Slash_Dont_Exists()
[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 @@ public void Test_Sftp_ChangeDirectory_Subfolder_Dont_Exists()
[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 @@ public void Test_Sftp_ChangeDirectory_Subfolder_With_Slash_Dont_Exists()
[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 @@ public void Test_Sftp_ChangeDirectory_Which_Exists()
[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 @@ public void Test_Sftp_ChangeDirectory_Which_Exists_With_Slash()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.CreateDirectory.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.CreateDirectory.cs
new file mode 100644
index 000000000..43b176697
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.CreateDirectory.cs
@@ -0,0 +1,72 @@
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+ ///
+ /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+ ///
+ 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();
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs
new file mode 100644
index 000000000..30697ddce
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs
@@ -0,0 +1,71 @@
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+ ///
+ /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+ ///
+ 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();
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Download.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs
similarity index 70%
rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.Download.cs
rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs
index 41dbdf33f..318834e4c 100644
--- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Download.cs
+++ b/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
{
///
/// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
///
- 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 @@ public void Test_Sftp_Download_Forbidden()
[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 @@ public void Test_Sftp_Download_File_Not_Exists()
[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 @@ public void Test_Sftp_BeginDownloadFile_StreamIsNull()
[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 @@ public void Test_Sftp_BeginDownloadFile_FileNameIsWhiteSpace()
[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 @@ public void Test_Sftp_BeginDownloadFile_FileNameIsNull()
[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);
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs
new file mode 100644
index 000000000..e37e937f9
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs
@@ -0,0 +1,263 @@
+using System.Diagnostics;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+ ///
+ /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+ ///
+ 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);
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFile.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFile.cs
similarity index 69%
rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFile.cs
rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFile.cs
index e1685d099..e74a8e7ed 100644
--- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFile.cs
+++ b/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
{
///
/// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
///
- 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 @@ public void Test_Sftp_Rename_File()
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 @@ public void Test_Sftp_Rename_File()
[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 @@ public void Test_Sftp_RenameFile_Null()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFileAsync.cs
similarity index 72%
rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs
rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.RenameFileAsync.cs
index a24ec0942..ade91099c 100644
--- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs
+++ b/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
{
///
/// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
///
- 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 @@ public async Task Test_Sftp_RenameFileAsync()
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 @@ public async Task Test_Sftp_RenameFileAsync()
[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);
diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.SynchronizeDirectories.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.SynchronizeDirectories.cs
similarity index 78%
rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.SynchronizeDirectories.cs
rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.SynchronizeDirectories.cs
index 2c9ddb3e0..4964715ef 100644
--- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.SynchronizeDirectories.cs
+++ b/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
{
///
/// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
///
- 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 @@ public void Test_Sftp_SynchronizeDirectories()
[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 @@ public void Test_Sftp_BeginSynchronizeDirectories()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Upload.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs
similarity index 89%
rename from src/Renci.SshNet.Tests/Classes/SftpClientTest.Upload.cs
rename to src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs
index 2018c9f0d..91c248bd3 100644
--- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Upload.cs
+++ b/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
{
///
/// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
///
- 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 @@ public void Test_Sftp_Upload_And_Download_1MB_File()
[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 @@ public void Test_Sftp_Upload_Forbidden()
[TestMethod]
[TestCategory("Sftp")]
- [TestCategory("integration")]
public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each()
{
var maxFiles = 10;
@@ -94,8 +83,9 @@ public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each()
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();
@@ -210,6 +200,11 @@ public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each()
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 @@ public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each()
// 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 @@ public void Test_Sftp_Ensure_Async_Delegates_Called_For_BeginFileUpload_BeginFil
[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 @@ public void Test_Sftp_BeginUploadFile_StreamIsNull()
[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 @@ public void Test_Sftp_BeginUploadFile_FileNameIsWhiteSpace()
[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 @@ public void Test_Sftp_BeginUploadFile_FileNameIsNull()
[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);
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.cs
new file mode 100644
index 000000000..1c7def55b
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.cs
@@ -0,0 +1,68 @@
+using System.Security.Cryptography;
+
+using Renci.SshNet.Sftp;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+ ///
+ /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+ ///
+ [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();
+ }
+ }
+
+ ///
+ /// Helper class to help with upload and download testing
+ ///
+ 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; }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpFileTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpFileTest.cs
new file mode 100644
index 000000000..058442511
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpFileTest.cs
@@ -0,0 +1,120 @@
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+ ///
+ /// Represents SFTP file information
+ ///
+ [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();
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs
new file mode 100644
index 000000000..d852a093a
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs
@@ -0,0 +1,545 @@
+using System.Diagnostics;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
+{
+ ///
+ /// Represents SSH command that can be executed.
+ ///
+ [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();
+ }
+ }
+
+ ///
+ /// Tests for Issue 563.
+ ///
+ [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();
+ }
+ }
+
+ ///
+ ///A test for BeginExecute
+ ///
+ [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);
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/TripleDesCipherTest.cs b/src/Renci.SshNet.IntegrationTests/OldIntegrationTests/TripleDesCipherTest.cs
new file mode 100644
index 000000000..a9195cf5e
--- /dev/null
+++ b/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
+{
+ ///
+ /// Implements 3DES cipher algorithm.
+ ///
+ [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();
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs b/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs
new file mode 100644
index 000000000..83313f4a8
--- /dev/null
+++ b/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);
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/Program.cs b/src/Renci.SshNet.IntegrationTests/Program.cs
new file mode 100644
index 000000000..af2b60d51
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/Program.cs
@@ -0,0 +1,11 @@
+namespace Renci.SshNet.IntegrationTests
+{
+ class Program
+ {
+#if NETFRAMEWORK
+ private static void Main()
+ {
+ }
+#endif
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/RemoteSshd.cs b/src/Renci.SshNet.IntegrationTests/RemoteSshd.cs
new file mode 100644
index 000000000..c81bf96f5
--- /dev/null
+++ b/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);
+ }
+ }
+ }
+
+ ///
+ /// Specifies whether challenge-response authentication is allowed.
+ ///
+ /// to allow challenge-response authentication.
+ ///
+ /// The current instance.
+ ///
+ public RemoteSshdConfig WithChallengeResponseAuthentication(bool? value)
+ {
+ _config.ChallengeResponseAuthentication = value;
+ return this;
+ }
+
+ ///
+ /// Specifies whether to allow keyboard-interactive authentication.
+ ///
+ /// to allow keyboard-interactive authentication.
+ ///
+ /// The current instance.
+ ///
+ public RemoteSshdConfig WithKeyboardInteractiveAuthentication(bool value)
+ {
+ _config.KeyboardInteractiveAuthentication = value;
+ return this;
+ }
+
+ ///
+ /// Specifies whether sshd should print /etc/motd when a user logs in interactively.
+ ///
+ /// if sshd should print /etc/motd when a user logs in interactively.
+ ///
+ /// The current instance.
+ ///
+ public RemoteSshdConfig PrintMotd(bool? value = true)
+ {
+ _config.PrintMotd = value;
+ return this;
+ }
+
+ ///
+ /// Specifies whether TCP forwarding is permitted.
+ ///
+ /// to allow TCP forwarding.
+ ///
+ /// The current instance.
+ ///
+ 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;
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj b/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
similarity index 65%
rename from src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj
rename to src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
index 2fdbcfd74..ae3505f10 100644
--- a/src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj
+++ b/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
@@ -3,7 +3,6 @@
net7.0
enable
- enable
false
true
@@ -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);CS1591
+ $(NoWarn);CS1591;SYSLIB0021;SYSLIB1045;SYSLIB0014;IDE0220;IDE0010
+
+ TRACE;FEATURE_MSTEST_DATATEST;FEATURE_SOCKET_EAP;FEATURE_ENCODING_ASCII;FEATURE_THREAD_SLEEP;FEATURE_THREAD_THREADPOOL
+
+
-
+
+
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -44,7 +48,18 @@
+
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs b/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs
index 5db1fe51c..fc90554a5 100644
--- a/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs
+++ b/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs
@@ -1,6 +1,4 @@
-using Renci.SshNet;
-
-namespace IntegrationTests
+namespace Renci.SshNet.IntegrationTests
{
///
/// The SCP client integration tests
diff --git a/src/Renci.SshNet.IntegrationTests/ScpTests.cs b/src/Renci.SshNet.IntegrationTests/ScpTests.cs
new file mode 100644
index 000000000..2fd4ea0ce
--- /dev/null
+++ b/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);
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// https://github.com/sshnet/SSH.NET/issues/289
+ ///
+#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);
+ }
+ }
+ }
+ }
+
+ ///
+ /// https://github.com/sshnet/SSH.NET/issues/286
+ ///
+#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