From dd36706616ebe0ad31ec6737f498375e677fbd0d Mon Sep 17 00:00:00 2001 From: drieseng Date: Sat, 26 Mar 2022 08:41:38 +0100 Subject: [PATCH 1/4] Support configuration of PrintMotd and AllowTcpForwarding options. --- .../Common/RemoteSshdConfigExtensions.cs | 1 + src/SshNetTests/RemoteSshd.cs | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/SshNetTests/Common/RemoteSshdConfigExtensions.cs b/src/SshNetTests/Common/RemoteSshdConfigExtensions.cs index 9c66efb..6869127 100644 --- a/src/SshNetTests/Common/RemoteSshdConfigExtensions.cs +++ b/src/SshNetTests/Common/RemoteSshdConfigExtensions.cs @@ -11,6 +11,7 @@ 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) diff --git a/src/SshNetTests/RemoteSshd.cs b/src/SshNetTests/RemoteSshd.cs index 7652f65..5f530ab 100644 --- a/src/SshNetTests/RemoteSshd.cs +++ b/src/SshNetTests/RemoteSshd.cs @@ -102,6 +102,32 @@ public RemoteSshdConfig WithKeyboardInteractiveAuthentication(bool 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)); From 2b2a96235d2130807b98ccc212b278ad28ea0ef1 Mon Sep 17 00:00:00 2001 From: drieseng Date: Sat, 26 Mar 2022 08:42:06 +0100 Subject: [PATCH 2/4] Fix and extend public key authentication tests. --- .../PrivateKeyAuthenticationTests.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/SshNetTests/PrivateKeyAuthenticationTests.cs b/src/SshNetTests/PrivateKeyAuthenticationTests.cs index d0225ab..587db7e 100644 --- a/src/SshNetTests/PrivateKeyAuthenticationTests.cs +++ b/src/SshNetTests/PrivateKeyAuthenticationTests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet; +using SshNet.TestTools.OpenSSH; using SshNetTests.Common; namespace SshNetTests @@ -29,22 +30,46 @@ public void TearDown() [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(); - client.Disconnect(); } } + [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) From 14b9f06987d622a23687bbd4563b98fba4fa5285 Mon Sep 17 00:00:00 2001 From: drieseng Date: Sat, 26 Mar 2022 08:44:01 +0100 Subject: [PATCH 3/4] Fix port forwarding tests. Add intermittend output test for ShellStream. --- docker/DockerFile | 6 +- src/SshNetTests/SshTests.cs | 246 ++++++++++++++++++++---------------- 2 files changed, 140 insertions(+), 112 deletions(-) diff --git a/docker/DockerFile b/docker/DockerFile index 9c8f0cd..65221db 100644 --- a/docker/DockerFile +++ b/docker/DockerFile @@ -33,7 +33,11 @@ RUN apk add --no-cache syslog-ng && \ addgroup sshnetadm sudo && \ dos2unix /opt/sshnet/* && \ # install shadow package; we use chage command in this package to expire/unexpire password of the sshnet user - apk add --no-cache shadow + apk add --no-cache shadow && \ + # allow us to use telnet command; we use this in the remote port forwarding tests + apk --no-cache add busybox-extras && \ + # install full-fledged ps command + apk add --no-cache procps EXPOSE 22 22 diff --git a/src/SshNetTests/SshTests.cs b/src/SshNetTests/SshTests.cs index c3af625..fcccbc8 100644 --- a/src/SshNetTests/SshTests.cs +++ b/src/SshNetTests/SshTests.cs @@ -1,6 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet; +using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; +using SshNetTests.Common; using System; using System.Collections.Generic; using System.IO; @@ -17,12 +19,25 @@ public class SshTests : TestBase { private IConnectionInfoFactory _connectionInfoFactory; private IConnectionInfoFactory _adminConnectionInfoFactory; + private RemoteSshdConfig _remoteSshdConfig; [TestInitialize] public void SetUp() { _connectionInfoFactory = new LinuxVMConnectionFactory(); _adminConnectionInfoFactory = new LinuxAdminConnectionFactory(); + + _remoteSshdConfig = new RemoteSshd(_adminConnectionInfoFactory).OpenConfig(); + _remoteSshdConfig.AllowTcpForwarding() + .PrintMotd(false) + .Update() + .Restart(); + } + + [TestCleanup] + public void TearDown() + { + _remoteSshdConfig?.Reset(); } /// @@ -35,8 +50,14 @@ public void Ssh_ShellStream_Exit() { client.Connect(); - using (var shellStream = client.CreateShellStream("xterm", 80, 24, 800, 600, 1024)) + var terminalModes = new Dictionary + { + { TerminalModes.ECHO, 0 } + }; + + using (var shellStream = client.CreateShellStream("xterm", 80, 24, 800, 600, 1024, terminalModes)) { + shellStream.WriteLine("echo Hello!"); shellStream.WriteLine("exit"); Thread.Sleep(1000); @@ -55,13 +76,13 @@ public void Ssh_ShellStream_Exit() var line = shellStream.ReadLine(); Assert.IsNotNull(line); - Assert.IsTrue(line.Contains("exit"), line); + Assert.IsTrue(line.EndsWith("Hello!"), line); - // TODO: ReadLine should immediately return null when the channel has been closed (issue #672) + // TODO: ReadLine should return null when the buffer is empty and the channel has been closed (issue #672) try { - shellStream.ReadLine(); - Assert.Fail(); + line = shellStream.ReadLine(); + Assert.Fail(line); } catch (NullReferenceException) { @@ -71,6 +92,72 @@ public void Ssh_ShellStream_Exit() } } + /// + /// https://github.com/sshnet/SSH.NET/issues/63 + /// + [TestMethod] + public void Ssh_ShellStream_IntermittendOutput() + { + const string remoteFile = "/home/sshnet/test.sh"; + + var expectedResult = string.Join("\n", + "Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "Line 5 ", + "Line 6"); + + var scriptBuilder = new StringBuilder(); + scriptBuilder.Append("#!/bin/sh\n"); + scriptBuilder.Append("echo Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("sleep .5\n"); + scriptBuilder.Append("echo Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("echo Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("echo Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + scriptBuilder.Append("sleep 2\n"); + scriptBuilder.Append("echo \"Line 5 \"\n"); + scriptBuilder.Append("echo Line 6 \n"); + scriptBuilder.Append("exit 13\n"); + + using (var sshClient = new SshClient(_connectionInfoFactory.Create())) + { + sshClient.Connect(); + + CreateShellScript(_connectionInfoFactory, remoteFile, scriptBuilder.ToString()); + + try + { + var terminalModes = new Dictionary + { + { TerminalModes.ECHO, 0 } + }; + + using (var shellStream = sshClient.CreateShellStream("xterm", 80, 24, 800, 600, 1024, terminalModes)) + { + shellStream.WriteLine(remoteFile); + + using (var reader = new StreamReader(shellStream, new UTF8Encoding(false), false, 10)) + { + var lines = new List(); + string line = null; + while ((line = reader.ReadLine()) != null) + { + lines.Add(line); + } + + Assert.AreEqual(6, lines.Count, string.Join("\n", lines)); + Assert.AreEqual(expectedResult, string.Join("\n", lines)); + } + } + } + finally + { + RemoveFileOrDirectory(sshClient, remoteFile); + } + } + } + /// /// Issue 1555 /// @@ -176,8 +263,7 @@ public void Ssh_Command_IntermittendOutput_OutputStream() "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Line 5 ", - "Line 6", - ""); + "Line 6"); var scriptBuilder = new StringBuilder(); scriptBuilder.Append("#!/bin/sh\n"); @@ -241,10 +327,9 @@ public void Ssh_Command_IntermittendOutput_OutputStream() public void Ssh_DynamicPortForwarding_DisposeSshClientWithoutStoppingPort() { const string searchText = "HTTP/1.1 301 Moved Permanently"; - const string hostName = "www.amazon.com"; + const string hostName = "github.com"; - var httpGetRequest = Encoding.ASCII.GetBytes($"GET /null HTTP/1.1\r\nHost: {hostName}\r\n\r\n"); - var httpResponseBuffer = new byte[2048]; + var httpGetRequest = Encoding.ASCII.GetBytes($"GET / HTTP/1.1\r\nHost: {hostName}\r\n\r\n"); Socket socksSocket; using (var client = new SshClient(_connectionInfoFactory.Create())) @@ -253,7 +338,7 @@ public void Ssh_DynamicPortForwarding_DisposeSshClientWithoutStoppingPort() client.Connect(); var forwardedPort = new ForwardedPortDynamic(1080); - forwardedPort.Exception += (sender, args) => Console.WriteLine("BROL" + args.Exception.ToString()); + forwardedPort.Exception += (sender, args) => Console.WriteLine(args.Exception.ToString()); client.AddForwardedPort(forwardedPort); forwardedPort.Start(); @@ -264,37 +349,28 @@ public void Ssh_DynamicPortForwarding_DisposeSshClientWithoutStoppingPort() socksSocket = socksClient.Connect(hostName, 80); socksSocket.Send(httpGetRequest); - var bytesReceived = socksSocket.Receive(httpResponseBuffer, - 0, - httpResponseBuffer.Length, - SocketFlags.None); - using (var sr = new StringReader(Encoding.ASCII.GetString(httpResponseBuffer, 0, bytesReceived))) - { - var firstLine = sr.ReadLine(); - - Assert.AreEqual(searchText, firstLine); - } + var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); } Assert.IsTrue(socksSocket.Connected); // check if client socket was properly closed - Assert.AreEqual(0, socksSocket.Receive(httpResponseBuffer, 0, httpResponseBuffer.Length, SocketFlags.None)); + Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None)); } [TestMethod] public void Ssh_DynamicPortForwarding_DomainName() { const string searchText = "HTTP/1.1 301 Moved Permanently"; - const string hostName = "www.amazon.com"; + const string hostName = "github.com"; // Set-up a host alias for google.be on the remote server that is not known locally; this allows us to // verify whether the host name is resolved remotely. const string hostNameAlias = "dynamicportforwarding-test.for.sshnet"; // Construct a HTTP request for which we expected the response to contain the search text. - var httpGetRequest = Encoding.ASCII.GetBytes($"GET /null HTTP/1.1\r\nHost: {hostName}\r\n\r\n"); - var httpResponseBuffer = new byte[2048]; + var httpGetRequest = Encoding.ASCII.GetBytes($"GET / HTTP/1.1\r\nHost: {hostName}\r\n\r\n"); var ipAddresses = Dns.GetHostAddresses(hostName); var hostsFileUpdated = AddOrUpdateHostsEntry(_adminConnectionInfoFactory, ipAddresses[0], hostNameAlias); @@ -317,18 +393,20 @@ public void Ssh_DynamicPortForwarding_DomainName() var socksSocket = socksClient.Connect(hostNameAlias, 80); socksSocket.Send(httpGetRequest); - Assert.IsTrue(SocketResponseContains(socksSocket, httpResponseBuffer, searchText)); + var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); // Verify if port is still open socksSocket.Send(httpGetRequest); - Assert.IsTrue(SocketResponseContains(socksSocket, httpResponseBuffer, searchText)); + httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); forwardedPort.Stop(); Assert.IsTrue(socksSocket.Connected); // check if client socket was properly closed - Assert.AreEqual(0, socksSocket.Receive(httpResponseBuffer, 0, httpResponseBuffer.Length, SocketFlags.None)); + Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None)); forwardedPort.Start(); @@ -336,14 +414,15 @@ public void Ssh_DynamicPortForwarding_DomainName() socksSocket = socksClient.Connect(hostNameAlias, 80); socksSocket.Send(httpGetRequest); - Assert.IsTrue(SocketResponseContains(socksSocket, httpResponseBuffer, searchText)); + httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); forwardedPort.Dispose(); Assert.IsTrue(socksSocket.Connected); // check if client socket was properly closed - Assert.AreEqual(0, socksSocket.Receive(httpResponseBuffer, 0, httpResponseBuffer.Length, SocketFlags.None)); + Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None)); forwardedPort.Dispose(); } @@ -361,7 +440,7 @@ public void Ssh_DynamicPortForwarding_DomainName() public void Ssh_DynamicPortForwarding_IPv4() { const string searchText = "HTTP/1.1 301 Moved Permanently"; - const string hostName = "www.amazon.com"; + const string hostName = "github.com"; var httpGetRequest = Encoding.ASCII.GetBytes($"GET /null HTTP/1.1\r\nHost: {hostName}\r\n\r\n"); var httpResponseBuffer = new byte[2048]; @@ -385,18 +464,13 @@ public void Ssh_DynamicPortForwarding_IPv4() var socksSocket = socksClient.Connect(new IPEndPoint(ipv4, 80)); socksSocket.Send(httpGetRequest); - - var bytesReceived = socksSocket.Receive(httpResponseBuffer, 0, httpResponseBuffer.Length, SocketFlags.None); - using (var sr = new StringReader(Encoding.ASCII.GetString(httpResponseBuffer, 0, bytesReceived))) - { - var firstLine = sr.ReadLine(); - - Assert.AreEqual(searchText, firstLine); - } + var httpResponse = GetHttpResponse(socksSocket, Encoding.ASCII); + Assert.IsTrue(httpResponse.Contains(searchText), httpResponse); forwardedPort.Dispose(); - Assert.AreEqual(0, socksSocket.Receive(httpResponseBuffer, 0, httpResponseBuffer.Length, SocketFlags.None)); + // check if client socket was properly closed + Assert.AreEqual(0, socksSocket.Receive(new byte[1], 0, 1, SocketFlags.None)); } } @@ -407,7 +481,7 @@ public void Ssh_DynamicPortForwarding_IPv4() public void Ssh_LocalPortForwardingCloseChannels() { const string hostNameAlias = "localportforwarding-test.for.sshnet"; - const string hostName = "www.amazon.com"; + const string hostName = "github.com"; var ipAddress = Dns.GetHostAddresses(hostName)[0]; @@ -435,7 +509,7 @@ public void Ssh_LocalPortForwardingCloseChannels() try { - var httpRequest = (HttpWebRequest) WebRequest.Create("http://" + localEndPoint + "/null"); + var httpRequest = (HttpWebRequest) WebRequest.Create("http://" + localEndPoint); httpRequest.Host = hostName; httpRequest.Method = "GET"; httpRequest.AllowAutoRedirect = false; @@ -458,34 +532,6 @@ public void Ssh_LocalPortForwardingCloseChannels() Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode); } } - - - /* - if (!httpResponse.ContentType.StartsWith("text/html")) - { - Console.WriteLine(@"Expected 'text/html' and optionally the character, but was '{0}'.", - httpResponse.ContentType); - return false; - } - - var responseStream = httpResponse.GetResponseStream(); - if (responseStream == null) - { - Console.WriteLine(@"ResponseStream is null."); - return false; - } - - using (var sr = new StreamReader(responseStream)) - { - var responseText = sr.ReadToEnd(); - if (!responseText.Contains("301 Moved Permanently")) - { - Console.WriteLine( - $@"Response does not contain 'The document has moved': {responseText}"); - return false; - } - } - */ } finally { @@ -507,7 +553,7 @@ public void Ssh_LocalPortForwardingCloseChannels() public void Ssh_LocalPortForwarding() { const string hostNameAlias = "localportforwarding-test.for.sshnet"; - const string hostName = "www.amazon.com"; + const string hostName = "github.com"; var ipAddress = Dns.GetHostAddresses(hostName)[0]; @@ -532,7 +578,7 @@ public void Ssh_LocalPortForwarding() try { - var httpRequest = (HttpWebRequest) WebRequest.Create("http://" + localEndPoint + "/null"); + var httpRequest = (HttpWebRequest) WebRequest.Create("http://" + localEndPoint); httpRequest.Host = hostName; httpRequest.Method = "GET"; httpRequest.Accept = "text/html"; @@ -556,33 +602,6 @@ public void Ssh_LocalPortForwarding() Assert.AreEqual(HttpStatusCode.MovedPermanently, httpResponse.StatusCode); } } - - /* - if (!httpResponse.ContentType.StartsWith("text/html")) - { - Console.WriteLine(@"Expected 'text/html' and optionally the character, but was '{0}'.", - httpResponse.ContentType); - return false; - } - - var responseStream = httpResponse.GetResponseStream(); - if (responseStream == null) - { - Console.WriteLine(@"ResponseStream is null."); - return false; - } - - using (var sr = new StreamReader(responseStream)) - { - var responseText = sr.ReadToEnd(); - if (!responseText.Contains("301 Moved Permanently")) - { - Console.WriteLine( - $@"Response does not contain 'The document has moved': {responseText}"); - return false; - } - } - */ } finally { @@ -642,7 +661,7 @@ public void Ssh_RemotePortForwarding() using (var s = client.CreateShellStream("a", 80, 25, 800, 600, 200)) { s.WriteLine($"telnet {forwardedPort1.BoundHost} {forwardedPort1.BoundPort}"); - s.Expect("Escape character is '^]'."); + s.Expect($"Connected to {forwardedPort1.BoundHost}\r\n"); s.WriteLine("ABC"); s.Flush(); s.Expect("ABC"); @@ -652,7 +671,7 @@ public void Ssh_RemotePortForwarding() using (var s = client.CreateShellStream("b", 80, 25, 800, 600, 200)) { s.WriteLine($"telnet {forwardedPort2.BoundHost} {forwardedPort2.BoundPort}"); - s.Expect("Escape character is '^]'."); + s.Expect($"Connected to {forwardedPort2.BoundHost}\r\n"); s.WriteLine("DEF"); s.Flush(); s.Expect("DEF"); @@ -674,7 +693,7 @@ public void Ssh_RemotePortForwarding() /// Issue 1591 /// [TestMethod] - public void Ssh_ExecuteBashScript() + public void Ssh_ExecuteShellScript() { const string remoteFile = "/home/sshnet/run.sh"; const string content = "#\bin\bash\necho Hello World!"; @@ -711,7 +730,7 @@ public void Ssh_ExecuteBashScript() var runLs = client.RunCommand("ls " + remoteFile); var asyncResultLs = runLs.BeginExecute(); - var runScript = client.RunCommand("bash " + remoteFile); + var runScript = client.RunCommand(remoteFile); var asyncResultScript = runScript.BeginExecute(); Assert.IsTrue(asyncResultScript.AsyncWaitHandle.WaitOne(10000)); @@ -906,26 +925,31 @@ private static bool RemoveHostsEntry(IConnectionInfoFactory linuxAdminConnection } } - private static bool SocketResponseContains(Socket socket, byte[] httpResponseBuffer, string searchText) + private static string GetHttpResponse(Socket socket, Encoding encoding) { + var httpResponseBuffer = new byte[2048]; + // We expect: // * The response to contain the searchText in the first receive. // * The full response to be returned in the first receive. var bytesReceived = socket.Receive(httpResponseBuffer, - 0, - httpResponseBuffer.Length, - SocketFlags.None); + 0, + httpResponseBuffer.Length, + SocketFlags.None); if (bytesReceived == 0) { - return false; + return null; } - using (var sr = new StringReader(Encoding.ASCII.GetString(httpResponseBuffer, 0, bytesReceived))) + if (bytesReceived == httpResponseBuffer.Length) { - var content = sr.ReadToEnd(); + throw new Exception("We expect the HTTP response to be less than the buffer size. If not, we won't consume the full response."); + } - return content.Contains(searchText); + using (var sr = new StringReader(encoding.GetString(httpResponseBuffer, 0, bytesReceived))) + { + return sr.ReadToEnd(); } } From 11fc454631d988be71746dfaf1d91db5de5021b1 Mon Sep 17 00:00:00 2001 From: drieseng Date: Sat, 26 Mar 2022 09:22:27 +0100 Subject: [PATCH 4/4] Add a few tests for SftpClient.Open(...). --- src/SshNetTests/SftpTests.cs | 216 ++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 2 deletions(-) diff --git a/src/SshNetTests/SftpTests.cs b/src/SshNetTests/SftpTests.cs index c203a31..733250a 100644 --- a/src/SshNetTests/SftpTests.cs +++ b/src/SshNetTests/SftpTests.cs @@ -4846,8 +4846,220 @@ public void Sftp_Open_Append_Write_FileDoesNotExist() } } + + + [TestMethod] + public void Sftp_Open_PathAndMode_ModeIsCreate_FileDoesNotExist() + { + const string remoteFile = "/home/sshnet/test"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + #region Verify if merely opening the file for create creates a zero-byte file + + using (client.Open(remoteFile, FileMode.Create)) + { + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + #endregion Verify if merely opening the file for create creates a zero-byte file + + client.DeleteFile(remoteFile); + + #region Verify if content is actually written to the file + + var content = GenerateRandom(100); + + using (var s = client.Open(remoteFile, FileMode.Create)) + { + s.Write(content, 0, content.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloaded)); + } + + #endregion Verify if content is actually written to the file + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_PathAndMode_ModeIsCreate_ExistingFile() + { + const string remoteFile = "/home/sshnet/test"; + const int fileSize = 5 * 1024; + var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + using (var input = CreateMemoryStream(fileSize)) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + input.Position = 0; + client.UploadFile(input, remoteFile); + + using (var stream = client.Open(remoteFile, FileMode.Create)) + { + // Verify if merely opening the file for create overwrites the file + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + stream.Write(newContent, 0, newContent.Length); + stream.Position = 0; + + Assert.AreEqual(CreateHash(newContent), CreateHash(stream)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsReadWrite_FileDoesNotExist() + { + const string remoteFile = "/home/sshnet/test"; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + #region Verify if merely opening the file for create creates a zero-byte file + + using (client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite)) + { + } + + Assert.IsTrue(client.Exists(remoteFile)); + + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + #endregion Verify if merely opening the file for create creates a zero-byte file + + client.DeleteFile(remoteFile); + + #region Verify if content is actually written to the file + + var content = GenerateRandom(100); + + using (var s = client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite)) + { + s.Write(content, 0, content.Length); + } + + using (var downloaded = new MemoryStream()) + { + client.DownloadFile(remoteFile, downloaded); + downloaded.Position = 0; + Assert.AreEqual(CreateHash(content), CreateHash(downloaded)); + } + + #endregion Verify if content is actually written to the file + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + + [TestMethod] + public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsReadWrite_ExistingFile() + { + const string remoteFile = "/home/sshnet/test"; + const int fileSize = 5 * 1024; + var newContent = new byte[] { 0x07, 0x03, 0x02, 0x0b }; + + using (var client = new SftpClient(_connectionInfoFactory.Create())) + using (var input = CreateMemoryStream(fileSize)) + { + client.Connect(); + + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + + try + { + input.Position = 0; + client.UploadFile(input, remoteFile); + + using (var stream = client.Open(remoteFile, FileMode.Create, FileAccess.ReadWrite)) + { + // Verify if merely opening the file for create overwrites the file + var attributes = client.GetAttributes(remoteFile); + Assert.IsTrue(attributes.IsRegularFile); + Assert.AreEqual(0L, attributes.Size); + + stream.Write(newContent, 0, newContent.Length); + stream.Position = 0; + + Assert.AreEqual(CreateHash(newContent), CreateHash(stream)); + } + } + finally + { + if (client.Exists(remoteFile)) + { + client.DeleteFile(remoteFile); + } + } + } + } + [TestMethod] - public void Sftp_Open_Create_Write_ExistingFile() + public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsWrite_ExistingFile() { const string remoteFile = "/home/sshnet/test"; @@ -4893,7 +5105,7 @@ public void Sftp_Open_Create_Write_ExistingFile() } [TestMethod] - public void Sftp_Open_Create_Write_FileDoesNotExist() + public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsWrite_FileDoesNotExist() { const string remoteFile = "/home/sshnet/test";