From b219168a7db436955ed1ed7256e019ee173cd41a Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 17 Feb 2024 11:20:33 +0800 Subject: [PATCH 01/17] Integrate `ZLibStream` from .NET 6.0+ with SSH.NET. --- src/Renci.SshNet/Compression/ZlibStream.cs | 47 ++++++++---------- src/Renci.SshNet/ConnectionInfo.cs | 4 ++ src/Renci.SshNet/Security/KeyExchange.cs | 4 +- .../CompressionTests.cs | 48 +++++++++++++++++++ 4 files changed, 75 insertions(+), 28 deletions(-) create mode 100644 test/Renci.SshNet.IntegrationTests/CompressionTests.cs diff --git a/src/Renci.SshNet/Compression/ZlibStream.cs b/src/Renci.SshNet/Compression/ZlibStream.cs index e09244992..65f1739c4 100644 --- a/src/Renci.SshNet/Compression/ZlibStream.cs +++ b/src/Renci.SshNet/Compression/ZlibStream.cs @@ -1,41 +1,40 @@ using System.IO; -#pragma warning disable S125 // Sections of code should not be commented out -#pragma warning disable SA1005 // Single line comments should begin with single space - namespace Renci.SshNet.Compression { /// /// Implements Zlib compression algorithm. /// #pragma warning disable CA1711 // Identifiers should not have incorrect suffix +#pragma warning disable CA1001 // Types that own disposable fields should be disposable public class ZlibStream +#pragma warning restore CA1001 // Types that own disposable fields should be disposable #pragma warning restore CA1711 // Identifiers should not have incorrect suffix { - //private readonly Ionic.Zlib.ZlibStream _baseStream; +#if NET6_0_OR_GREATER + private readonly System.IO.Compression.ZLibStream _baseStream; +#endif /// /// Initializes a new instance of the class. /// /// The stream. /// The mode. -#pragma warning disable IDE0060 // Remove unused parameter public ZlibStream(Stream stream, CompressionMode mode) -#pragma warning restore IDE0060 // Remove unused parameter { - //switch (mode) - //{ - // case CompressionMode.Compress: - // this._baseStream = new Ionic.Zlib.ZlibStream(stream, Ionic.Zlib.CompressionMode.Compress, Ionic.Zlib.CompressionLevel.Default); - // break; - // case CompressionMode.Decompress: - // this._baseStream = new Ionic.Zlib.ZlibStream(stream, Ionic.Zlib.CompressionMode.Decompress, Ionic.Zlib.CompressionLevel.Default); - // break; - // default: - // break; - //} - - //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; +#if NET6_0_OR_GREATER + switch (mode) + { + case CompressionMode.Compress: + _baseStream = new System.IO.Compression.ZLibStream(stream, System.IO.Compression.CompressionMode.Compress); + break; + case CompressionMode.Decompress: + _baseStream = new System.IO.Compression.ZLibStream(stream, System.IO.Compression.CompressionMode.Decompress); + break; + default: + break; + } +#endif } /// @@ -44,15 +43,11 @@ public ZlibStream(Stream stream, CompressionMode mode) /// The buffer. /// The offset. /// The count. -#pragma warning disable IDE0060 // Remove unused parameter public void Write(byte[] buffer, int offset, int count) -#pragma warning restore IDE0060 // Remove unused parameter { - //this._baseStream.Write(buffer, offset, count); +#if NET6_0_OR_GREATER + _baseStream.Write(buffer, offset, count); +#endif } -#pragma warning restore SA1005 // Single line comments should begin with single space } } - -#pragma warning restore SA1005 // Single line comments should begin with single space -#pragma warning restore S125 // Sections of code should not be commented out diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index d57c08860..f6f2d2284 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -414,6 +414,10 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy CompressionAlgorithms = new Dictionary> { { "none", null }, +#if NET6_0_OR_GREATER + { "zlib@openssh.com", () => new ZlibOpenSsh() }, + { "zlib", () => new Zlib() }, +#endif }; ChannelRequests = new Dictionary diff --git a/src/Renci.SshNet/Security/KeyExchange.cs b/src/Renci.SshNet/Security/KeyExchange.cs index 10f7e0f8a..1dd09ea97 100644 --- a/src/Renci.SshNet/Security/KeyExchange.cs +++ b/src/Renci.SshNet/Security/KeyExchange.cs @@ -124,7 +124,7 @@ from a in message.MacAlgorithmsServerToClient var compressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsClientToServer where a == b - select a).LastOrDefault(); + select a).FirstOrDefault(); if (string.IsNullOrEmpty(compressionAlgorithmName)) { throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed); @@ -136,7 +136,7 @@ from a in message.CompressionAlgorithmsClientToServer var decompressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsServerToClient where a == b - select a).LastOrDefault(); + select a).FirstOrDefault(); if (string.IsNullOrEmpty(decompressionAlgorithmName)) { throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed); diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs new file mode 100644 index 000000000..3da5bcfe4 --- /dev/null +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -0,0 +1,48 @@ +using Renci.SshNet.Compression; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class CompressionTests : IntegrationTestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + } + + [TestMethod] + public void None() + { + DoTest(new KeyValuePair>("none", null)); + } + +#if NET6_0_OR_GREATER + [TestMethod] + public void Zlib() + { + DoTest(new KeyValuePair>("zlib", () => new Zlib())); + } + + [TestMethod] + public void ZlibOpenSsh() + { + DoTest(new KeyValuePair>("zlib@openssh.com", () => new ZlibOpenSsh())); + } +#endif + + private void DoTest(KeyValuePair> compressor) + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.ConnectionInfo.CompressionAlgorithms.Clear(); + client.ConnectionInfo.CompressionAlgorithms.Add(compressor); + + client.Connect(); + client.Disconnect(); + } + } + } +} From beafd8222a6f3b85d181515e7896c5b72de60e9b Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 17 Feb 2024 13:59:54 +0800 Subject: [PATCH 02/17] OpenSSH server does not support zlib (pre-auth); OpenSSH client still supports zlib (pre-auth) --- test/Renci.SshNet.IntegrationTests/CompressionTests.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs index 3da5bcfe4..976140c65 100644 --- a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -20,12 +20,6 @@ public void None() } #if NET6_0_OR_GREATER - [TestMethod] - public void Zlib() - { - DoTest(new KeyValuePair>("zlib", () => new Zlib())); - } - [TestMethod] public void ZlibOpenSsh() { From 37dc55c05968cdf0120d5893dbd804b07b73731f Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 17 Feb 2024 14:34:00 +0800 Subject: [PATCH 03/17] Correct compression algorithm name; Update README.md --- README.md | 8 ++++++++ src/Renci.SshNet/Compression/ZlibOpenSsh.cs | 4 ++-- test/Renci.SshNet.IntegrationTests/CompressionTests.cs | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e39e4f298..1a0d17b06 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,14 @@ Private keys can be encrypted using one of the following cipher methods: * hmac-sha2-256-etm@openssh.com * hmac-sha2-512-etm@openssh.com + +## Compression + +**SSH.NET** supports the following compression algorithms: +* none (default) +* zlib@openssh.com (.NET 6 and higher) +* zlib (.NET 6 and higher) + ## Framework Support **SSH.NET** supports the following target frameworks: * .NETFramework 4.6.2 (and higher) diff --git a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs index 45bf1165f..981f74048 100644 --- a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs +++ b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs @@ -3,7 +3,7 @@ namespace Renci.SshNet.Compression { /// - /// Represents "zlib@openssh.org" compression implementation. + /// Represents "zlib@openssh.com" compression implementation. /// public class ZlibOpenSsh : Compressor { @@ -12,7 +12,7 @@ public class ZlibOpenSsh : Compressor /// public override string Name { - get { return "zlib@openssh.org"; } + get { return "zlib@openssh.com"; } } /// diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs index 976140c65..bb60b51e6 100644 --- a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -24,7 +24,7 @@ public void None() public void ZlibOpenSsh() { DoTest(new KeyValuePair>("zlib@openssh.com", () => new ZlibOpenSsh())); - } + } #endif private void DoTest(KeyValuePair> compressor) From 8fb67f7199d03ff3a76dc64937eadeb71b5af617 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 17 Feb 2024 11:20:33 +0800 Subject: [PATCH 04/17] Integrate `ZLibStream` from .NET 6.0+ with SSH.NET. --- src/Renci.SshNet/Compression/ZlibStream.cs | 47 ++++++++---------- src/Renci.SshNet/ConnectionInfo.cs | 4 ++ src/Renci.SshNet/Security/KeyExchange.cs | 4 +- .../CompressionTests.cs | 48 +++++++++++++++++++ 4 files changed, 75 insertions(+), 28 deletions(-) create mode 100644 test/Renci.SshNet.IntegrationTests/CompressionTests.cs diff --git a/src/Renci.SshNet/Compression/ZlibStream.cs b/src/Renci.SshNet/Compression/ZlibStream.cs index e09244992..65f1739c4 100644 --- a/src/Renci.SshNet/Compression/ZlibStream.cs +++ b/src/Renci.SshNet/Compression/ZlibStream.cs @@ -1,41 +1,40 @@ using System.IO; -#pragma warning disable S125 // Sections of code should not be commented out -#pragma warning disable SA1005 // Single line comments should begin with single space - namespace Renci.SshNet.Compression { /// /// Implements Zlib compression algorithm. /// #pragma warning disable CA1711 // Identifiers should not have incorrect suffix +#pragma warning disable CA1001 // Types that own disposable fields should be disposable public class ZlibStream +#pragma warning restore CA1001 // Types that own disposable fields should be disposable #pragma warning restore CA1711 // Identifiers should not have incorrect suffix { - //private readonly Ionic.Zlib.ZlibStream _baseStream; +#if NET6_0_OR_GREATER + private readonly System.IO.Compression.ZLibStream _baseStream; +#endif /// /// Initializes a new instance of the class. /// /// The stream. /// The mode. -#pragma warning disable IDE0060 // Remove unused parameter public ZlibStream(Stream stream, CompressionMode mode) -#pragma warning restore IDE0060 // Remove unused parameter { - //switch (mode) - //{ - // case CompressionMode.Compress: - // this._baseStream = new Ionic.Zlib.ZlibStream(stream, Ionic.Zlib.CompressionMode.Compress, Ionic.Zlib.CompressionLevel.Default); - // break; - // case CompressionMode.Decompress: - // this._baseStream = new Ionic.Zlib.ZlibStream(stream, Ionic.Zlib.CompressionMode.Decompress, Ionic.Zlib.CompressionLevel.Default); - // break; - // default: - // break; - //} - - //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; +#if NET6_0_OR_GREATER + switch (mode) + { + case CompressionMode.Compress: + _baseStream = new System.IO.Compression.ZLibStream(stream, System.IO.Compression.CompressionMode.Compress); + break; + case CompressionMode.Decompress: + _baseStream = new System.IO.Compression.ZLibStream(stream, System.IO.Compression.CompressionMode.Decompress); + break; + default: + break; + } +#endif } /// @@ -44,15 +43,11 @@ public ZlibStream(Stream stream, CompressionMode mode) /// The buffer. /// The offset. /// The count. -#pragma warning disable IDE0060 // Remove unused parameter public void Write(byte[] buffer, int offset, int count) -#pragma warning restore IDE0060 // Remove unused parameter { - //this._baseStream.Write(buffer, offset, count); +#if NET6_0_OR_GREATER + _baseStream.Write(buffer, offset, count); +#endif } -#pragma warning restore SA1005 // Single line comments should begin with single space } } - -#pragma warning restore SA1005 // Single line comments should begin with single space -#pragma warning restore S125 // Sections of code should not be commented out diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index d57c08860..f6f2d2284 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -414,6 +414,10 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy CompressionAlgorithms = new Dictionary> { { "none", null }, +#if NET6_0_OR_GREATER + { "zlib@openssh.com", () => new ZlibOpenSsh() }, + { "zlib", () => new Zlib() }, +#endif }; ChannelRequests = new Dictionary diff --git a/src/Renci.SshNet/Security/KeyExchange.cs b/src/Renci.SshNet/Security/KeyExchange.cs index 10f7e0f8a..1dd09ea97 100644 --- a/src/Renci.SshNet/Security/KeyExchange.cs +++ b/src/Renci.SshNet/Security/KeyExchange.cs @@ -124,7 +124,7 @@ from a in message.MacAlgorithmsServerToClient var compressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsClientToServer where a == b - select a).LastOrDefault(); + select a).FirstOrDefault(); if (string.IsNullOrEmpty(compressionAlgorithmName)) { throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed); @@ -136,7 +136,7 @@ from a in message.CompressionAlgorithmsClientToServer var decompressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsServerToClient where a == b - select a).LastOrDefault(); + select a).FirstOrDefault(); if (string.IsNullOrEmpty(decompressionAlgorithmName)) { throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed); diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs new file mode 100644 index 000000000..3da5bcfe4 --- /dev/null +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -0,0 +1,48 @@ +using Renci.SshNet.Compression; + +namespace Renci.SshNet.IntegrationTests +{ + [TestClass] + public class CompressionTests : IntegrationTestBase + { + private IConnectionInfoFactory _connectionInfoFactory; + + [TestInitialize] + public void SetUp() + { + _connectionInfoFactory = new LinuxVMConnectionFactory(SshServerHostName, SshServerPort); + } + + [TestMethod] + public void None() + { + DoTest(new KeyValuePair>("none", null)); + } + +#if NET6_0_OR_GREATER + [TestMethod] + public void Zlib() + { + DoTest(new KeyValuePair>("zlib", () => new Zlib())); + } + + [TestMethod] + public void ZlibOpenSsh() + { + DoTest(new KeyValuePair>("zlib@openssh.com", () => new ZlibOpenSsh())); + } +#endif + + private void DoTest(KeyValuePair> compressor) + { + using (var client = new SshClient(_connectionInfoFactory.Create())) + { + client.ConnectionInfo.CompressionAlgorithms.Clear(); + client.ConnectionInfo.CompressionAlgorithms.Add(compressor); + + client.Connect(); + client.Disconnect(); + } + } + } +} From 776274846aefa8634ce9c5b7644b92f853160029 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 17 Feb 2024 13:59:54 +0800 Subject: [PATCH 05/17] OpenSSH server does not support zlib (pre-auth); OpenSSH client still supports zlib (pre-auth) --- test/Renci.SshNet.IntegrationTests/CompressionTests.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs index 3da5bcfe4..976140c65 100644 --- a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -20,12 +20,6 @@ public void None() } #if NET6_0_OR_GREATER - [TestMethod] - public void Zlib() - { - DoTest(new KeyValuePair>("zlib", () => new Zlib())); - } - [TestMethod] public void ZlibOpenSsh() { From e7cdd329efabd258cdbfa4c45894aa198158df33 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 17 Feb 2024 14:34:00 +0800 Subject: [PATCH 06/17] Correct compression algorithm name; Update README.md --- README.md | 8 ++++++++ src/Renci.SshNet/Compression/ZlibOpenSsh.cs | 4 ++-- test/Renci.SshNet.IntegrationTests/CompressionTests.cs | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e39e4f298..1a0d17b06 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,14 @@ Private keys can be encrypted using one of the following cipher methods: * hmac-sha2-256-etm@openssh.com * hmac-sha2-512-etm@openssh.com + +## Compression + +**SSH.NET** supports the following compression algorithms: +* none (default) +* zlib@openssh.com (.NET 6 and higher) +* zlib (.NET 6 and higher) + ## Framework Support **SSH.NET** supports the following target frameworks: * .NETFramework 4.6.2 (and higher) diff --git a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs index 45bf1165f..981f74048 100644 --- a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs +++ b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs @@ -3,7 +3,7 @@ namespace Renci.SshNet.Compression { /// - /// Represents "zlib@openssh.org" compression implementation. + /// Represents "zlib@openssh.com" compression implementation. /// public class ZlibOpenSsh : Compressor { @@ -12,7 +12,7 @@ public class ZlibOpenSsh : Compressor /// public override string Name { - get { return "zlib@openssh.org"; } + get { return "zlib@openssh.com"; } } /// diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs index 976140c65..bb60b51e6 100644 --- a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -24,7 +24,7 @@ public void None() public void ZlibOpenSsh() { DoTest(new KeyValuePair>("zlib@openssh.com", () => new ZlibOpenSsh())); - } + } #endif private void DoTest(KeyValuePair> compressor) From e7fcad3fe665c4b387afc0a469252768108f073e Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 16 Mar 2024 20:36:49 +0800 Subject: [PATCH 07/17] Test the compression by upload/download file --- .../CompressionTests.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs index bb60b51e6..b391731fc 100644 --- a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -29,13 +29,28 @@ public void ZlibOpenSsh() private void DoTest(KeyValuePair> compressor) { - using (var client = new SshClient(_connectionInfoFactory.Create())) + using (var scpClient = new ScpClient(_connectionInfoFactory.Create())) { - client.ConnectionInfo.CompressionAlgorithms.Clear(); - client.ConnectionInfo.CompressionAlgorithms.Add(compressor); + scpClient.ConnectionInfo.CompressionAlgorithms.Clear(); + scpClient.ConnectionInfo.CompressionAlgorithms.Add(compressor); - client.Connect(); - client.Disconnect(); + scpClient.Connect(); + + Assert.AreEqual(compressor.Key, scpClient.ConnectionInfo.CurrentClientCompressionAlgorithm); + Assert.AreEqual(compressor.Key, scpClient.ConnectionInfo.CurrentServerCompressionAlgorithm); + + var file = $"/tmp/{Guid.NewGuid()}.txt"; + var fileContent = "RepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeating"; + + using var uploadStream = new MemoryStream(Encoding.UTF8.GetBytes(fileContent)); + scpClient.Upload(uploadStream, file); + + using var downloadStream = new MemoryStream(); + scpClient.Download(file, downloadStream); + + var result = Encoding.UTF8.GetString(downloadStream.ToArray()); + + Assert.AreEqual(fileContent, result); } } } From 29f502c917cf104a03cfc1b727ca60e16efafd18 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Thu, 21 Mar 2024 08:56:25 +0800 Subject: [PATCH 08/17] Refactor compression. --- .../Compression/CompressionMode.cs | 18 ---- src/Renci.SshNet/Compression/Compressor.cs | 102 +++++------------- src/Renci.SshNet/Compression/Zlib.cs | 30 +++++- src/Renci.SshNet/Compression/ZlibOpenSsh.cs | 7 +- src/Renci.SshNet/Compression/ZlibStream.cs | 53 --------- 5 files changed, 57 insertions(+), 153 deletions(-) delete mode 100644 src/Renci.SshNet/Compression/CompressionMode.cs delete mode 100644 src/Renci.SshNet/Compression/ZlibStream.cs diff --git a/src/Renci.SshNet/Compression/CompressionMode.cs b/src/Renci.SshNet/Compression/CompressionMode.cs deleted file mode 100644 index b0b6f6b94..000000000 --- a/src/Renci.SshNet/Compression/CompressionMode.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Renci.SshNet.Compression -{ - /// - /// Specifies compression modes. - /// - public enum CompressionMode - { - /// - /// Specifies that content should be compressed. - /// - Compress = 0, - - /// - /// Specifies that content should be decompressed. - /// - Decompress = 1, - } -} diff --git a/src/Renci.SshNet/Compression/Compressor.cs b/src/Renci.SshNet/Compression/Compressor.cs index 63eb3e336..dfc9752f7 100644 --- a/src/Renci.SshNet/Compression/Compressor.cs +++ b/src/Renci.SshNet/Compression/Compressor.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using Renci.SshNet.Security; @@ -8,14 +7,8 @@ namespace Renci.SshNet.Compression /// /// Represents base class for compression algorithm implementation. /// - public abstract class Compressor : Algorithm, IDisposable + public abstract class Compressor : Algorithm { - private readonly ZlibStream _compressor; - private readonly ZlibStream _decompressor; - private MemoryStream _compressorStream; - private MemoryStream _decompressorStream; - private bool _isDisposed; - /// /// Gets or sets a value indicating whether compression is active. /// @@ -29,18 +22,6 @@ public abstract class Compressor : Algorithm, IDisposable /// protected Session Session { get; private set; } - /// - /// Initializes a new instance of the class. - /// - protected Compressor() - { - _compressorStream = new MemoryStream(); - _decompressorStream = new MemoryStream(); - - _compressor = new ZlibStream(_compressorStream, CompressionMode.Compress); - _decompressor = new ZlibStream(_decompressorStream, CompressionMode.Decompress); - } - /// /// Initializes the algorithm. /// @@ -57,7 +38,7 @@ public virtual void Init(Session session) /// /// The compressed data. /// - public virtual byte[] Compress(byte[] data) + public byte[] Compress(byte[] data) { return Compress(data, 0, data.Length); } @@ -85,13 +66,20 @@ public virtual byte[] Compress(byte[] data, int offset, int length) return buffer; } - _compressorStream.SetLength(0); - - _compressor.Write(data, offset, length); - - return _compressorStream.ToArray(); + return CompressCore(data, offset, length); } + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// The zero-based byte offset in at which to begin reading the data to compress. + /// The number of bytes to be compressed. + /// + /// The compressed data. + /// + protected abstract byte[] CompressCore(byte[] data, int offset, int length); + /// /// Decompresses the specified data. /// @@ -99,7 +87,7 @@ public virtual byte[] Compress(byte[] data, int offset, int length) /// /// The decompressed data. /// - public virtual byte[] Decompress(byte[] data) + public byte[] Decompress(byte[] data) { return Decompress(data, 0, data.Length); } @@ -127,60 +115,18 @@ public virtual byte[] Decompress(byte[] data, int offset, int length) return buffer; } - _decompressorStream.SetLength(0); - - _decompressor.Write(data, offset, length); - - return _decompressorStream.ToArray(); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); + return DecompressCore(data, offset, length); } /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// to release both managed and unmanaged resources; to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (_isDisposed) - { - return; - } - - if (disposing) - { - var compressorStream = _compressorStream; - if (compressorStream != null) - { - compressorStream.Dispose(); - _compressorStream = null; - } - - var decompressorStream = _decompressorStream; - if (decompressorStream != null) - { - decompressorStream.Dispose(); - _decompressorStream = null; - } - - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the is reclaimed - /// by garbage collection. + /// Decompresses the specified data. /// - ~Compressor() - { - Dispose(disposing: false); - } + /// Compressed data. + /// The zero-based byte offset in at which to begin reading the data to decompress. + /// The number of bytes to be read from the compressed data. + /// + /// The decompressed data. + /// + protected abstract byte[] DecompressCore(byte[] data, int offset, int length); } } diff --git a/src/Renci.SshNet/Compression/Zlib.cs b/src/Renci.SshNet/Compression/Zlib.cs index 504697fd7..2c9b0bd18 100644 --- a/src/Renci.SshNet/Compression/Zlib.cs +++ b/src/Renci.SshNet/Compression/Zlib.cs @@ -1,9 +1,13 @@ -namespace Renci.SshNet.Compression +#if NET6_0_OR_GREATER +using System.IO; +using System.IO.Compression; + +namespace Renci.SshNet.Compression { /// /// Represents "zlib" compression implementation. /// - internal sealed class Zlib : Compressor + internal class Zlib : Compressor { /// /// Gets algorithm name. @@ -23,5 +27,27 @@ public override void Init(Session session) IsActive = true; } + + protected override byte[] CompressCore(byte[] data, int offset, int length) + { + using var outputStream = new MemoryStream(); + using var zlibStream = new ZLibStream(outputStream, CompressionMode.Compress); + + zlibStream.Write(data, offset, length); + + return outputStream.ToArray(); + } + + protected override byte[] DecompressCore(byte[] data, int offset, int length) + { + using var inputStream = new MemoryStream(data, offset, length); + using var zlibStream = new ZLibStream(inputStream, CompressionMode.Decompress); + + using var outputStream = new MemoryStream(); + zlibStream.CopyTo(outputStream); + + return outputStream.ToArray(); + } } } +#endif diff --git a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs index 981f74048..be9a3372f 100644 --- a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs +++ b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs @@ -1,11 +1,12 @@ -using Renci.SshNet.Messages.Authentication; +#if NET6_0_OR_GREATER +using Renci.SshNet.Messages.Authentication; namespace Renci.SshNet.Compression { /// /// Represents "zlib@openssh.com" compression implementation. /// - public class ZlibOpenSsh : Compressor + internal sealed class ZlibOpenSsh : Zlib { /// /// Gets algorithm name. @@ -23,6 +24,7 @@ public override void Init(Session session) { base.Init(session); + IsActive = false; session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; } @@ -33,3 +35,4 @@ private void Session_UserAuthenticationSuccessReceived(object sender, MessageEve } } } +#endif diff --git a/src/Renci.SshNet/Compression/ZlibStream.cs b/src/Renci.SshNet/Compression/ZlibStream.cs deleted file mode 100644 index 65f1739c4..000000000 --- a/src/Renci.SshNet/Compression/ZlibStream.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.IO; - -namespace Renci.SshNet.Compression -{ - /// - /// Implements Zlib compression algorithm. - /// -#pragma warning disable CA1711 // Identifiers should not have incorrect suffix -#pragma warning disable CA1001 // Types that own disposable fields should be disposable - public class ZlibStream -#pragma warning restore CA1001 // Types that own disposable fields should be disposable -#pragma warning restore CA1711 // Identifiers should not have incorrect suffix - { -#if NET6_0_OR_GREATER - private readonly System.IO.Compression.ZLibStream _baseStream; -#endif - - /// - /// Initializes a new instance of the class. - /// - /// The stream. - /// The mode. - public ZlibStream(Stream stream, CompressionMode mode) - { -#if NET6_0_OR_GREATER - switch (mode) - { - case CompressionMode.Compress: - _baseStream = new System.IO.Compression.ZLibStream(stream, System.IO.Compression.CompressionMode.Compress); - break; - case CompressionMode.Decompress: - _baseStream = new System.IO.Compression.ZLibStream(stream, System.IO.Compression.CompressionMode.Decompress); - break; - default: - break; - } -#endif - } - - /// - /// Writes the specified buffer. - /// - /// The buffer. - /// The offset. - /// The count. - public void Write(byte[] buffer, int offset, int count) - { -#if NET6_0_OR_GREATER - _baseStream.Write(buffer, offset, count); -#endif - } - } -} From e2b697531a584e0b8e3d65b2ad4a940d95445b50 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Thu, 21 Mar 2024 20:15:03 +0800 Subject: [PATCH 09/17] Move delayed compression logic to base class. --- src/Renci.SshNet/Compression/Compressor.cs | 44 +++++++++++++++------ src/Renci.SshNet/Compression/Zlib.cs | 19 ++++----- src/Renci.SshNet/Compression/ZlibOpenSsh.cs | 38 ------------------ src/Renci.SshNet/ConnectionInfo.cs | 4 +- 4 files changed, 42 insertions(+), 63 deletions(-) delete mode 100644 src/Renci.SshNet/Compression/ZlibOpenSsh.cs diff --git a/src/Renci.SshNet/Compression/Compressor.cs b/src/Renci.SshNet/Compression/Compressor.cs index dfc9752f7..bb2e27e7b 100644 --- a/src/Renci.SshNet/Compression/Compressor.cs +++ b/src/Renci.SshNet/Compression/Compressor.cs @@ -1,5 +1,6 @@ using System; +using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Security; namespace Renci.SshNet.Compression @@ -9,18 +10,23 @@ namespace Renci.SshNet.Compression /// public abstract class Compressor : Algorithm { - /// - /// Gets or sets a value indicating whether compression is active. - /// - /// - /// if compression is active; otherwise, . - /// - protected bool IsActive { get; set; } + private readonly bool _delayedCompression; + + private bool _isActive; + private Session _session; /// - /// Gets the session. + /// Initializes a new instance of the class. /// - protected Session Session { get; private set; } + /// + /// to start compression after receiving SSH_MSG_NEWKEYS. + /// to delay compression util receiving SSH_MSG_USERAUTH_SUCCESS. + /// . + /// + protected Compressor(bool delayedCompression) + { + _delayedCompression = delayedCompression; + } /// /// Initializes the algorithm. @@ -28,7 +34,15 @@ public abstract class Compressor : Algorithm /// The session. public virtual void Init(Session session) { - Session = session; + if (_delayedCompression) + { + _session = session; + _session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + else + { + _isActive = true; + } } /// @@ -54,7 +68,7 @@ public byte[] Compress(byte[] data) /// public virtual byte[] Compress(byte[] data, int offset, int length) { - if (!IsActive) + if (!_isActive) { if (offset == 0 && length == data.Length) { @@ -103,7 +117,7 @@ public byte[] Decompress(byte[] data) /// public virtual byte[] Decompress(byte[] data, int offset, int length) { - if (!IsActive) + if (!_isActive) { if (offset == 0 && length == data.Length) { @@ -128,5 +142,11 @@ public virtual byte[] Decompress(byte[] data, int offset, int length) /// The decompressed data. /// protected abstract byte[] DecompressCore(byte[] data, int offset, int length); + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + _isActive = true; + _session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } } } diff --git a/src/Renci.SshNet/Compression/Zlib.cs b/src/Renci.SshNet/Compression/Zlib.cs index 2c9b0bd18..36620a3fd 100644 --- a/src/Renci.SshNet/Compression/Zlib.cs +++ b/src/Renci.SshNet/Compression/Zlib.cs @@ -9,23 +9,20 @@ namespace Renci.SshNet.Compression /// internal class Zlib : Compressor { - /// - /// Gets algorithm name. - /// - public override string Name + private readonly string _name; + + public Zlib(bool delayedCompression) + : base(delayedCompression) { - get { return "zlib"; } + _name = delayedCompression ? "zlib@openssh.com" : "zlib"; } /// - /// Initializes the algorithm. + /// Gets algorithm name. /// - /// The session. - public override void Init(Session session) + public override string Name { - base.Init(session); - - IsActive = true; + get { return _name; } } protected override byte[] CompressCore(byte[] data, int offset, int length) diff --git a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs deleted file mode 100644 index be9a3372f..000000000 --- a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs +++ /dev/null @@ -1,38 +0,0 @@ -#if NET6_0_OR_GREATER -using Renci.SshNet.Messages.Authentication; - -namespace Renci.SshNet.Compression -{ - /// - /// Represents "zlib@openssh.com" compression implementation. - /// - internal sealed class ZlibOpenSsh : Zlib - { - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "zlib@openssh.com"; } - } - - /// - /// Initializes the algorithm. - /// - /// The session. - public override void Init(Session session) - { - base.Init(session); - - IsActive = false; - session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; - } - - private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) - { - IsActive = true; - Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; - } - } -} -#endif diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index 99ccc7154..dddc91818 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -440,8 +440,8 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy { { "none", null }, #if NET6_0_OR_GREATER - { "zlib@openssh.com", () => new ZlibOpenSsh() }, - { "zlib", () => new Zlib() }, + { "zlib@openssh.com", () => new Zlib(delayedCompression: true) }, + { "zlib", () => new Zlib(delayedCompression: false) }, #endif }; From 36672964f2ee01a5e9cb1dbaeb9a976f2d7901d8 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Thu, 21 Mar 2024 20:45:09 +0800 Subject: [PATCH 10/17] seal Zlib --- src/Renci.SshNet/Compression/Zlib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Renci.SshNet/Compression/Zlib.cs b/src/Renci.SshNet/Compression/Zlib.cs index 36620a3fd..bc3ab43e0 100644 --- a/src/Renci.SshNet/Compression/Zlib.cs +++ b/src/Renci.SshNet/Compression/Zlib.cs @@ -7,7 +7,7 @@ namespace Renci.SshNet.Compression /// /// Represents "zlib" compression implementation. /// - internal class Zlib : Compressor + internal sealed class Zlib : Compressor { private readonly string _name; From cb982293beac7003d3fbc28c10e5e5f714926e10 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Thu, 21 Mar 2024 20:50:28 +0800 Subject: [PATCH 11/17] update unit test --- test/Renci.SshNet.IntegrationTests/CompressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs index b391731fc..b04b66315 100644 --- a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -23,7 +23,7 @@ public void None() [TestMethod] public void ZlibOpenSsh() { - DoTest(new KeyValuePair>("zlib@openssh.com", () => new ZlibOpenSsh())); + DoTest(new KeyValuePair>("zlib@openssh.com", () => new Zlib(delayedCompression: true))); } #endif From e7ff21e1e628dfeb6c1f3a514862dc24cc6d9975 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 23 Mar 2024 17:06:25 +0800 Subject: [PATCH 12/17] update unit test to see if it can trigger integration test --- test/Renci.SshNet.IntegrationTests/CompressionTests.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs index b04b66315..151525c27 100644 --- a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -40,7 +40,14 @@ private void DoTest(KeyValuePair> compressor) Assert.AreEqual(compressor.Key, scpClient.ConnectionInfo.CurrentServerCompressionAlgorithm); var file = $"/tmp/{Guid.NewGuid()}.txt"; - var fileContent = "RepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeatingRepeating"; + + var sb = new StringBuilder(); + for (var i = 0; i < 100; i++) + { + _ = sb.Append("Repeating"); + } + + var fileContent = sb.ToString(); using var uploadStream = new MemoryStream(Encoding.UTF8.GetBytes(fileContent)); scpClient.Upload(uploadStream, file); From 7ebf78c81923ccae62e0a2876e0aefc57c4f4653 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 23 Mar 2024 22:55:57 +0800 Subject: [PATCH 13/17] Flush zlibStream --- src/Renci.SshNet/Compression/Zlib.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Renci.SshNet/Compression/Zlib.cs b/src/Renci.SshNet/Compression/Zlib.cs index bc3ab43e0..a9d6ace7b 100644 --- a/src/Renci.SshNet/Compression/Zlib.cs +++ b/src/Renci.SshNet/Compression/Zlib.cs @@ -31,6 +31,7 @@ protected override byte[] CompressCore(byte[] data, int offset, int length) using var zlibStream = new ZLibStream(outputStream, CompressionMode.Compress); zlibStream.Write(data, offset, length); + zlibStream.Flush(); return outputStream.ToArray(); } From 16b07450cb95cccfc7a83298e19888c11a7830d9 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sun, 24 Mar 2024 11:05:47 +0800 Subject: [PATCH 14/17] Fix integration test --- src/Renci.SshNet/Compression/Compressor.cs | 38 ++++++++++- src/Renci.SshNet/Compression/Zlib.cs | 73 +++++++++++++++++---- src/Renci.SshNet/Compression/ZlibOpenSsh.cs | 17 +++++ src/Renci.SshNet/ConnectionInfo.cs | 4 +- src/Renci.SshNet/Session.cs | 14 ++++ 5 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 src/Renci.SshNet/Compression/ZlibOpenSsh.cs diff --git a/src/Renci.SshNet/Compression/Compressor.cs b/src/Renci.SshNet/Compression/Compressor.cs index bb2e27e7b..876144962 100644 --- a/src/Renci.SshNet/Compression/Compressor.cs +++ b/src/Renci.SshNet/Compression/Compressor.cs @@ -8,12 +8,13 @@ namespace Renci.SshNet.Compression /// /// Represents base class for compression algorithm implementation. /// - public abstract class Compressor : Algorithm + public abstract class Compressor : Algorithm, IDisposable { private readonly bool _delayedCompression; private bool _isActive; private Session _session; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -148,5 +149,40 @@ private void Session_UserAuthenticationSuccessReceived(object sender, MessageEve _isActive = true; _session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// to release both managed and unmanaged resources; to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + + if (disposing) + { + _isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the is reclaimed + /// by garbage collection. + /// + ~Compressor() + { + Dispose(disposing: false); + } } } diff --git a/src/Renci.SshNet/Compression/Zlib.cs b/src/Renci.SshNet/Compression/Zlib.cs index a9d6ace7b..29fe8aacb 100644 --- a/src/Renci.SshNet/Compression/Zlib.cs +++ b/src/Renci.SshNet/Compression/Zlib.cs @@ -7,14 +7,27 @@ namespace Renci.SshNet.Compression /// /// Represents "zlib" compression implementation. /// - internal sealed class Zlib : Compressor + internal class Zlib : Compressor { - private readonly string _name; + private readonly ZLibStream _compressor; + private readonly ZLibStream _decompressor; + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + private bool _isDisposed; - public Zlib(bool delayedCompression) + public Zlib() + : this(delayedCompression: false) + { + } + + protected Zlib(bool delayedCompression) : base(delayedCompression) { - _name = delayedCompression ? "zlib@openssh.com" : "zlib"; + _compressorStream = new MemoryStream(); + _decompressorStream = new MemoryStream(); + + _compressor = new ZLibStream(_compressorStream, CompressionMode.Compress); + _decompressor = new ZLibStream(_decompressorStream, CompressionMode.Decompress); } /// @@ -22,30 +35,64 @@ public Zlib(bool delayedCompression) /// public override string Name { - get { return _name; } + get { return "zlib"; } } protected override byte[] CompressCore(byte[] data, int offset, int length) { - using var outputStream = new MemoryStream(); - using var zlibStream = new ZLibStream(outputStream, CompressionMode.Compress); + _compressorStream.SetLength(0); - zlibStream.Write(data, offset, length); - zlibStream.Flush(); + _compressor.Write(data, offset, length); + _compressor.Flush(); - return outputStream.ToArray(); + return _compressorStream.ToArray(); } protected override byte[] DecompressCore(byte[] data, int offset, int length) { - using var inputStream = new MemoryStream(data, offset, length); - using var zlibStream = new ZLibStream(inputStream, CompressionMode.Decompress); + _decompressorStream.Write(data, offset, length); + _decompressorStream.Position = 0; using var outputStream = new MemoryStream(); - zlibStream.CopyTo(outputStream); + _decompressor.CopyTo(outputStream); + + _decompressorStream.SetLength(0); return outputStream.ToArray(); } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// to release both managed and unmanaged resources; to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (_isDisposed) + { + return; + } + + if (disposing) + { + var compressorStream = _compressorStream; + if (compressorStream != null) + { + compressorStream.Dispose(); + _compressorStream = null; + } + + var decompressorStream = _decompressorStream; + if (decompressorStream != null) + { + decompressorStream.Dispose(); + _decompressorStream = null; + } + + _isDisposed = true; + } + } } } #endif diff --git a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs new file mode 100644 index 000000000..6ece09254 --- /dev/null +++ b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs @@ -0,0 +1,17 @@ +#if NET6_0_OR_GREATER +namespace Renci.SshNet.Compression +{ + internal sealed class ZlibOpenSsh : Zlib + { + public ZlibOpenSsh() + : base(delayedCompression: true) + { + } + + public override string Name + { + get { return "zlib@openssh.com"; } + } + } +} +#endif diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index dddc91818..99ccc7154 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -440,8 +440,8 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy { { "none", null }, #if NET6_0_OR_GREATER - { "zlib@openssh.com", () => new Zlib(delayedCompression: true) }, - { "zlib", () => new Zlib(delayedCompression: false) }, + { "zlib@openssh.com", () => new ZlibOpenSsh() }, + { "zlib", () => new Zlib() }, #endif }; diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 0c067ec2d..dd20f34c9 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -2132,6 +2132,20 @@ protected virtual void Dispose(bool disposing) _clientMac = null; } + var serverDecompression = _serverDecompression; + if (serverDecompression != null) + { + serverDecompression.Dispose(); + _serverDecompression = null; + } + + var clientCompression = _clientCompression; + if (clientCompression != null) + { + clientCompression.Dispose(); + _clientCompression = null; + } + var keyExchange = _keyExchange; if (keyExchange != null) { From d5d74a1cafe736b318531feb3143dc8e72857b23 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sun, 24 Mar 2024 11:11:19 +0800 Subject: [PATCH 15/17] update test --- test/Renci.SshNet.IntegrationTests/CompressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs index 151525c27..68cf1b237 100644 --- a/test/Renci.SshNet.IntegrationTests/CompressionTests.cs +++ b/test/Renci.SshNet.IntegrationTests/CompressionTests.cs @@ -23,7 +23,7 @@ public void None() [TestMethod] public void ZlibOpenSsh() { - DoTest(new KeyValuePair>("zlib@openssh.com", () => new Zlib(delayedCompression: true))); + DoTest(new KeyValuePair>("zlib@openssh.com", () => new ZlibOpenSsh())); } #endif From 86ce31921d0df9960e9ebd0125e06560a92364fc Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Fri, 5 Apr 2024 09:43:35 +0800 Subject: [PATCH 16/17] Update ConnectionInfo.cs Co-authored-by: Rob Hague --- src/Renci.SshNet/ConnectionInfo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index 99ccc7154..2a0a76a33 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -441,7 +441,6 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy { "none", null }, #if NET6_0_OR_GREATER { "zlib@openssh.com", () => new ZlibOpenSsh() }, - { "zlib", () => new Zlib() }, #endif }; From 77c475d7529e0e7a05ce413542398ecb7b0f90e3 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Fri, 5 Apr 2024 09:44:57 +0800 Subject: [PATCH 17/17] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2fe71b5a6..b63460b76 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - ![Logo](https://raw.githubusercontent.com/sshnet/SSH.NET/develop/images/logo/png/SS-NET-icon-h50.png) SSH.NET + ![Logo](https://raw.githubusercontent.com/sshnet/SSH.NET/develop/images/logo/png/SS-NET-icon-h50.png) SSH.NET ======= SSH.NET is a Secure Shell (SSH-2) library for .NET, optimized for parallelism. @@ -126,7 +126,6 @@ Private keys can be encrypted using one of the following cipher methods: **SSH.NET** supports the following compression algorithms: * none (default) * zlib@openssh.com (.NET 6 and higher) -* zlib (.NET 6 and higher) ## Framework Support **SSH.NET** supports the following target frameworks: