New
2025.1.0
Highlights
- Add
DownloadFileAsyncandUploadFileAsynctoSftpClient(#1634) - Much improved performance of
SftpFileStreamin consecutive read (e.g.SftpFileStream.CopyTo) scenarios (#1705)
Breaking changes:
SftpFileStreampreviously had some incomplete synchronisation for multi-threaded access, but was not advertised nor fully functioning as thread safe. This synchronisation was removed in #1705. When accessing anSftpFileStreaminstance from multiple threads simultaneously, ensure there exists appropriate synchronisation.SftpClient.CreateTextandWriteAll{Bytes/Text/Lines}were changed in #1686 to truncate the file before writing if it exists, to align with the equivalent methods onSystem.IO.File. Given that the prior behaviour was 14 years old, the change treads the line between breaking change and bug fix.IEnumerable<string> ReadLinesonSftpClientwas updated in #1681 to download and yield lines during enumeration rather than reading them all up front and returning the result. This means that the connection must be active during enumeration. When storing the result ofReadLinesfor later use, consider usingstring[] ReadAllLinesinstead.
What's Changed
- Version 2025.0.0 by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1631
- Add a debug helper for reading traffic with Wireshark by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1627
- Update docs and remove a leftover DSA certificate algorithm by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1632
- Override more methods in PipeStream, ShellStream by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1637
- Read the underlying buffer in SshDataStream by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1638
- Add UploadFileAsync and DownloadFileAsync methods by @sdt-ndelarosa in https://github.com/sshnet/SSH.NET/pull/1634
- fix IDE0350 warnings with .NET SDK 9.0.300 by @mus65 in https://github.com/sshnet/SSH.NET/pull/1645
- Re-introduce ChangeWindow method on ShellStream by @Rasmus715 in https://github.com/sshnet/SSH.NET/pull/1646
- Fix hang in SftpClient.UploadFile upon error by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1643
- Drop netstandard2.1 target by @mus65 in https://github.com/sshnet/SSH.NET/pull/1647
- Added GetAttributesAsync to SftpClient by @deckertron9000 in https://github.com/sshnet/SSH.NET/pull/1648
- Bump alpine from 3.21 to 3.22 in /test/Renci.SshNet.IntegrationTests by @dependabot[bot] in https://github.com/sshnet/SSH.NET/pull/1651
- Bump dependencies by @dependabot[bot] in https://github.com/sshnet/SSH.NET/pull/1652
- Check host key algorithms before continuing key exchange by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1642
- Use an array buffer for the sftp packet stream by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1649
- Use ArraySegment for channel data by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1650
- scp: add flag to notify for uploading finished when uploaded an empty file by @thegame4craft in https://github.com/sshnet/SSH.NET/pull/1658
- Fixes spelling errors by @jacobslusser in https://github.com/sshnet/SSH.NET/pull/1668
- Bump the dependencies group with 5 updates by @dependabot[bot] in https://github.com/sshnet/SSH.NET/pull/1663
- Refactor logging to allow a loggerfactory per session by @desdesdes in https://github.com/sshnet/SSH.NET/pull/1673
- Adapt InternalUploadFile for async by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1653
- Use BouncyCastle for Diffie-Hellman key exchange by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1654
- Remove unnecessary SftpFileStream unit tests and dedup Open{Async} by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1680
- Bump Vampire/setup-wsl from 5.0.1 to 6.0.0 by @dependabot[bot] in https://github.com/sshnet/SSH.NET/pull/1683
- Use ReadExactly in ReadAllBytes and yield in ReadLines by @Rob-Hague in https://github.com/sshnet/SSH.NET/pull/1681
New Contributors
- @sdt-ndelarosa made their first contribution in https://github.com/sshnet/SSH.NET/pull/1634
- @Rasmus715 made their first contribution in https://github.com/sshnet/SSH.NET/pull/1646
- @deckertron9000 made their first contribution in https://github.com/sshnet/SSH.NET/pull/1648
- @thegame4craft made their first contribution in https://github.com/sshnet/SSH.NET/pull/1658
- @desdesdes made their first contribution in https://github.com/sshnet/SSH.NET/pull/1673
- @nikolamilekic made their first contribution in https://github.com/sshnet/SSH.NET/pull/1712
- @Copilot made their first contribution in https://github.com/sshnet/SSH.NET/pull/1719
Full Changelog: https://github.com/sshnet/SSH.NET/compare/2025.0.0...2025.1.0
API diff
namespace Renci.SshNet
{
public class ConnectionInfo
{
+ public Microsoft.Extensions.Logging.ILoggerFactory? LoggerFactory { get; set; }
}
public interface ISftpClient : Renci.SshNet.IBaseClient
{
+ System.Threading.Tasks.Task DownloadFileAsync(string path, System.IO.Stream output, System.Threading.CancellationToken cancellationToken = default);
+ System.Threading.Tasks.Task<Renci.SshNet.Sftp.SftpFileAttributes> GetAttributesAsync(string path, System.Threading.CancellationToken cancellationToken);
+ System.Threading.Tasks.Task UploadFileAsync(System.IO.Stream input, string path, System.Threading.CancellationToken cancellationToken = default);
}
public class SftpClient : Renci.SshNet.BaseClient, Renci.SshNet.ISftpClient, Renci.SshNet.IBaseClient
{
+ public System.Threading.Tasks.Task DownloadFileAsync(string path, System.IO.Stream output, System.Threading.CancellationToken cancellationToken = default);
+ public System.Threading.Tasks.Task<Renci.SshNet.Sftp.SftpFileAttributes> GetAttributesAsync(string path, System.Threading.CancellationToken cancellationToken);
+ public System.Threading.Tasks.Task UploadFileAsync(System.IO.Stream input, string path, System.Threading.CancellationToken cancellationToken = default);
}
public sealed class ShellStream
{
+ public void ChangeWindowSize(uint columns, uint rows, uint width, uint height);
}
}
namespace Renci.SshNet.Common
{
public class SftpPathNotFoundException : Renci.SshNet.Common.SftpException
{
+ public SftpPathNotFoundException(string? message, string? path, System.Exception? innerException);
+ public SftpPathNotFoundException(string? message, string? path);
+ public string? Path { get; }
}
+ public class SftpException : Renci.SshNet.Common.SshException
+ {
+ public SftpException(Renci.SshNet.Sftp.StatusCode statusCode, string? message, System.Exception? innerException);
+ public SftpException(Renci.SshNet.Sftp.StatusCode statusCode, string? message);
+ public SftpException(Renci.SshNet.Sftp.StatusCode statusCode);
+ public Renci.SshNet.Sftp.StatusCode StatusCode { get; }
+ }
}
namespace Renci.SshNet.Security
{
+ public class KeyExchangeDiffieHellman : Renci.SshNet.Security.KeyExchange
+ {
+ public KeyExchangeDiffieHellman(string name, Org.BouncyCastle.Crypto.Parameters.DHParameters parameters, System.Security.Cryptography.HashAlgorithmName hashAlgorithm);
+ public override void Start(Renci.SshNet.Session session, Renci.SshNet.Messages.Transport.KeyExchangeInitMessage message, bool sendClientInitMessage);
+ public override string Name { get; }
+ }
+ public class KeyExchangeDiffieHellmanGroupExchange : Renci.SshNet.Security.KeyExchange
+ {
+ public KeyExchangeDiffieHellmanGroupExchange(string name, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, uint minimumGroupSize, uint preferredGroupSize, uint maximumGroupSize);
+ public KeyExchangeDiffieHellmanGroupExchange(string name, System.Security.Cryptography.HashAlgorithmName hashAlgorithm);
+ public override void Start(Renci.SshNet.Session session, Renci.SshNet.Messages.Transport.KeyExchangeInitMessage message, bool sendClientInitMessage);
+ public override string Name { get; }
+ }
}
namespace Renci.SshNet.Sftp
{
public sealed class SftpFileAttributes
{
+ public bool IsGroupIDBitSet { get; set; }
+ public bool IsStickyBitSet { get; set; }
+ public bool IsUIDBitSet { get; set; }
}
+ public enum StatusCode
+ {
+ Ok = 0,
+ Eof = 1,
+ NoSuchFile = 2,
+ PermissionDenied = 3,
+ Failure = 4,
+ BadMessage = 5,
+ NoConnection = 6,
+ ConnectionLost = 7,
+ OperationUnsupported = 8,
+ }
}