Bot releases are hidden (Show)
ShellStream
has been completely rewritten, all bugs fixed and performance improved.SshCommand
hmac-ripemd160
[email protected]
Closed
event to ShellStream
. by @scott-xu in https://github.com/sshnet/SSH.NET/pull/1332
Full Changelog: https://github.com/sshnet/SSH.NET/compare/2023.0.1...2024.0.0
Published by WojciechNagorski 10 months ago
Full Changelog: https://github.com/sshnet/SSH.NET/compare/2023.0.0...2023.0.1
Published by WojciechNagorski about 1 year ago
SftpClient
and SftpFileStream
ISftpFile
interface to SftpFile
Thank you to all of our contributors for making SSH.NET the best SSH library for .NET!
Full Changelog: https://github.com/sshnet/SSH.NET/compare/2020.0.1...2023.0.0
API listing follows standard diff formatting.
Lines preceded by a '+' are additions and a '-' indicates removal.
namespace Renci.SshNet {
- public abstract class BaseClient : IDisposable {
+ public abstract class BaseClient : IBaseClient, IDisposable {
+ public Task ConnectAsync(CancellationToken cancellationToken);
}
+ public interface IBaseClient
+ public interface IPrivateKeySource
- public interface ISftpClient {
+ public interface ISftpClient : IBaseClient, IDisposable {
+ Task DeleteFileAsync(string path, CancellationToken cancellationToken);
- IEnumerable<SftpFile> EndListDirectory(IAsyncResult asyncResult);
+ IEnumerable<ISftpFile> EndListDirectory(IAsyncResult asyncResult);
- SftpFile Get(string path);
+ ISftpFile Get(string path);
+ Task<SftpFileSytemInformation> GetStatusAsync(string path, CancellationToken cancellationToken);
- IEnumerable<SftpFile> ListDirectory(string path, Action<int> listCallback = null);
+ IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallback = null);
+ IAsyncEnumerable<ISftpFile> ListDirectoryAsync(string path, CancellationToken cancellationToken);
+ Task<SftpFileStream> OpenAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken);
+ Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken);
+ void SetLastAccessTime(string path, DateTime lastAccessTime);
+ void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc);
+ void SetLastWriteTime(string path, DateTime lastWriteTime);
+ void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc);
}
public class NetConfClient : BaseClient {
+ public NetConfClient(string host, int port, string username, params IPrivateKeySource[] keyFiles);
- public NetConfClient(string host, int port, string username, params PrivateKeyFile[] keyFiles);
+ public NetConfClient(string host, string username, params IPrivateKeySource[] keyFiles);
- public NetConfClient(string host, string username, params PrivateKeyFile[] keyFiles);
}
public class PrivateKeyAuthenticationMethod : AuthenticationMethod, IDisposable {
+ public PrivateKeyAuthenticationMethod(string username, params IPrivateKeySource[] keyFiles);
- public PrivateKeyAuthenticationMethod(string username, params PrivateKeyFile[] keyFiles);
- public ICollection<PrivateKeyFile> KeyFiles { get; private set; }
+ public ICollection<IPrivateKeySource> KeyFiles { get; private set; }
}
public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable {
+ public PrivateKeyConnectionInfo(string host, int port, string username, params IPrivateKeySource[] keyFiles);
- public PrivateKeyConnectionInfo(string host, int port, string username, params PrivateKeyFile[] keyFiles);
+ public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles);
- public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles);
+ public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles);
- public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles);
+ public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles);
- public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles);
+ public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles);
- public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles);
+ public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles);
- public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles);
+ public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles);
- public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles);
- public ICollection<PrivateKeyFile> KeyFiles { get; private set; }
+ public ICollection<IPrivateKeySource> KeyFiles { get; private set; }
}
- public class PrivateKeyFile : IDisposable {
+ public class PrivateKeyFile : IDisposable, IPrivateKeySource {
+ public PrivateKeyFile(Key key);
- public HostAlgorithm HostKey { get; private set; }
+ public IReadOnlyCollection<HostAlgorithm> HostKeyAlgorithms { get; }
+ public Key Key { get; }
}
public class ScpClient : BaseClient {
+ public ScpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles);
- public ScpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles);
+ public ScpClient(string host, string username, params IPrivateKeySource[] keyFiles);
- public ScpClient(string host, string username, params PrivateKeyFile[] keyFiles);
}
public class Session : IDisposable, ISession {
+ public Task ConnectAsync(CancellationToken cancellationToken);
}
- public class SftpClient : BaseClient, ISftpClient {
+ public class SftpClient : BaseClient, IBaseClient, IDisposable, ISftpClient {
+ public SftpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles);
- public SftpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles);
+ public SftpClient(string host, string username, params IPrivateKeySource[] keyFiles);
- public SftpClient(string host, string username, params PrivateKeyFile[] keyFiles);
+ public Task DeleteFileAsync(string path, CancellationToken cancellationToken);
- public IEnumerable<SftpFile> EndListDirectory(IAsyncResult asyncResult);
+ public IEnumerable<ISftpFile> EndListDirectory(IAsyncResult asyncResult);
- public SftpFile Get(string path);
+ public ISftpFile Get(string path);
+ public Task<SftpFileSytemInformation> GetStatusAsync(string path, CancellationToken cancellationToken);
- public IEnumerable<SftpFile> ListDirectory(string path, Action<int> listCallback = null);
+ public IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallback = null);
+ public IAsyncEnumerable<ISftpFile> ListDirectoryAsync(string path, CancellationToken cancellationToken);
+ public Task<SftpFileStream> OpenAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken);
+ public Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken);
}
public class SshClient : BaseClient {
+ public SshClient(string host, int port, string username, params IPrivateKeySource[] keyFiles);
- public SshClient(string host, int port, string username, params PrivateKeyFile[] keyFiles);
+ public SshClient(string host, string username, params IPrivateKeySource[] keyFiles);
- public SshClient(string host, string username, params PrivateKeyFile[] keyFiles);
}
}
namespace Renci.SshNet.Common {
public struct BigInteger : IComparable, IComparable<BigInteger>, IEquatable<BigInteger>, IFormattable {
- public string ToString(string format, IFormatProvider provider);
+ public string ToString(string format, IFormatProvider formatProvider);
}
public class HostKeyEventArgs : EventArgs {
+ public string FingerPrintMD5 { get; }
+ public string FingerPrintSHA256 { get; }
}
public class NetConfServerException : SshException {
- protected NetConfServerException(SerializationInfo info, StreamingContext context);
}
public class ProxyException : SshException {
- protected ProxyException(SerializationInfo info, StreamingContext context);
}
public class ScpException : SshException {
- protected ScpException(SerializationInfo info, StreamingContext context);
}
public class SemaphoreLight : IDisposable {
- ~SemaphoreLight();
}
public class SftpPathNotFoundException : SshException {
- protected SftpPathNotFoundException(SerializationInfo info, StreamingContext context);
}
public class SftpPermissionDeniedException : SshException {
- protected SftpPermissionDeniedException(SerializationInfo info, StreamingContext context);
}
public class SshAuthenticationException : SshException {
- protected SshAuthenticationException(SerializationInfo info, StreamingContext context);
}
public class SshConnectionException : SshException {
- protected SshConnectionException(SerializationInfo info, StreamingContext context);
}
public class SshException : Exception {
- protected SshException(SerializationInfo info, StreamingContext context);
}
public class SshOperationTimeoutException : SshException {
- protected SshOperationTimeoutException(SerializationInfo info, StreamingContext context);
}
public class SshPassPhraseNullOrEmptyException : SshException {
- protected SshPassPhraseNullOrEmptyException(SerializationInfo info, StreamingContext context);
}
}
namespace Renci.SshNet.Messages.Connection {
public class ChannelDataMessage : ChannelMessage {
- public int Offset { get; set; }
+ public int Offset { get; private set; }
- public int Size { get; set; }
+ public int Size { get; private set; }
}
}
namespace Renci.SshNet.Security {
public class DsaKey : Key, IDisposable {
- protected override DigitalSignature DigitalSignature { get; }
+ protected internal override DigitalSignature DigitalSignature { get; }
}
public class EcdsaKey : Key, IDisposable {
- protected override DigitalSignature DigitalSignature { get; }
+ protected internal override DigitalSignature DigitalSignature { get; }
- public CngAlgorithm HashAlgorithm { get; }
+ public HashAlgorithmName HashAlgorithm { get; }
+ public byte[] PrivateKey { get; private set; }
}
public class ED25519Key : Key, IDisposable {
+ public ED25519Key(byte[] pk);
- protected override DigitalSignature DigitalSignature { get; }
+ protected internal override DigitalSignature DigitalSignature { get; }
}
public abstract class Key {
+ public string Comment { get; set; }
- protected abstract DigitalSignature DigitalSignature { get; }
+ protected internal abstract DigitalSignature DigitalSignature { get; }
}
public class KeyHostAlgorithm : HostAlgorithm {
+ public KeyHostAlgorithm(string name, Key key, DigitalSignature digitalSignature);
+ public KeyHostAlgorithm(string name, Key key, byte[] data, DigitalSignature digitalSignature);
+ public DigitalSignature DigitalSignature { get; private set; }
}
public class RsaKey : Key, IDisposable {
- protected override DigitalSignature DigitalSignature { get; }
+ protected internal override DigitalSignature DigitalSignature { get; }
+ public override string ToString();
}
}
namespace Renci.SshNet.Security.Cryptography {
public abstract class BlockCipher : SymmetricCipher {
- public override byte[] Decrypt(byte[] data);
+ public override byte[] Decrypt(byte[] input);
- public override byte[] Decrypt(byte[] data, int offset, int length);
+ public override byte[] Decrypt(byte[] input, int offset, int length);
- public override byte[] Encrypt(byte[] data, int offset, int length);
+ public override byte[] Encrypt(byte[] input, int offset, int length);
}
public class RsaDigitalSignature : CipherDigitalSignature, IDisposable {
+ public RsaDigitalSignature(RsaKey rsaKey, HashAlgorithmName hashAlgorithmName);
}
}
namespace Renci.SshNet.Security.Cryptography.Ciphers {
public class RsaCipher : AsymmetricCipher {
- public override byte[] Decrypt(byte[] data);
+ public override byte[] Decrypt(byte[] input);
- public override byte[] Decrypt(byte[] data, int offset, int length);
+ public override byte[] Decrypt(byte[] input, int offset, int length);
- public override byte[] Encrypt(byte[] data, int offset, int length);
+ public override byte[] Encrypt(byte[] input, int offset, int length);
}
}
namespace Renci.SshNet.Sftp {
+ public interface ISftpFile
- public class SftpFile
+ public sealed class SftpFile : ISftpFile
public class SftpFileStream : Stream {
+ public override Task FlushAsync(CancellationToken cancellationToken);
+ public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
}
}
Published by drieseng over 2 years ago
This release fixes a security vulnerability in our X25519 key exchange that could allow an attacker to eavesdrop the
communications to decrypt them.
More information is available in advisory CVE-2022-29245.
Published by drieseng over 3 years ago
Support LF as line ending for banner and identification string
Even though RFC 4253 requires that an identification string MUST be terminated by a carriage return and line feed, this fix restores support for banners and identification strings that are only terminated by a line feed.
This is a workaround for an issue in version 7.4 of OpenSSH which was fixed in version 7.5.
Fixes issue #761.
While our list of supported target frameworks is impressive, it does come with a cost. Some of these target frameworks are no longer supported by Microsoft and even required software that is no longer available for download.
We'd like to gather feedback from our users through this issue to learn how important it is to continue supporting all these target frameworks.
Do you want to keep track of general progress and annoucements for SSH.NET? You can now follow us on Twitter.
Do you or your company rely on SSH.NET in your projects? If you want to encourage us to keep on going and show us that you appreciate our work, please consider becoming a sponsor through GitHub Sponsors.
Published by drieseng almost 4 years ago
This release of SSH.NET adds support for .NET Standard 2.0.
This brings the full list of the supported target frameworks to:
Fixes issue #357, #436 and #474.
SSH.NET now supports the following additional key exchange algorithms:
Fixes issue #53, #406 and #504.
The following additional host key algorithms are now supported:
SSH.NET now supports the following private key formats:
Fixes issue #485.
Until now any issue related to Protocol Version Exchange would be reported using a single message:
Server response does not contain SSH protocol identification.
As of this release, we identify - and report - three distinct issues:
The server immediately closed the connections upon reception of our SSH identification string.
The server response does not contain an SSH identification string.
The connection to the remote server was closed before any data was received.
More information on the Protocol Version Exchange is available here:
https://tools.ietf.org/html/rfc4253#section-4.2
The identification string of the server contains a null character (which is prohibited).
The server response contains a null character at position 0x00000005:
00000000 ED 95 9C 21 00 ...!.
A server must not send a null character before the Protocol Version Exchange is complete.
More information is available here:
https://tools.ietf.org/html/rfc4253#section-4.2
The server response does not contain an SSH identification string.
The server response does not contain an SSH identification string:
00000000 48 54 54 50 2F 31 2E 30 20 32 30 30 20 4F 4B 0D HTTP/1.0 200 OK.
00000010 0A 43 6F 6E 74 65 6E 74 2D 4C 65 6E 67 74 68 3A .Content-Length:
00000020 20 31 30 0D 0A 43 6F 6E 74 65 6E 74 2D 54 79 70 10..Content-Typ
00000030 65 3A 20 61 70 70 6C 69 63 61 74 69 6F 6E 2F 6F e: application/o
00000040 63 74 65 74 2D 73 74 72 65 61 6D 0D 0A ctet-stream..
More information on the Protocol Version Exchange is available here:
https://tools.ietf.org/html/rfc4253#section-4.2
A ChannelCloseTimeout property has been introduced on ConnectionInfo that controls the timeout to apply when waiting for a server to acknowledge closing a channel. The default value is 1 second.
If a server does not report to our SSH_MSG_CHANNEL_CLOSE message with a SSH_MSG_CHANNEL_CLOSE message before the specified timeout elapses, we'll stop waiting and consider the channel to be closed.
Fixes issue #335.
CVE-2018-20685 caused OpenSSH to implement more strict validation of file paths. ScpClient was updated to correctly deal with these changes.
In case of a recursive directory upload, ScpClient cannot fully absorb the impact of these changes and as such Upload(DirectoryInfo directoryInfo, string path)
now fails if path does not exist on the remote host. For the same reason, the mode and modification/access time will also no longer be set on path.
The mode and modification/access time continue to be set on any files or subdirectories that are uploaded.
Connect()
times out when server rejects initializing subsystem (issue #308).Disconnect()
and Dispose()
may block indefinitely on .NET Core (issue #355 and #686).While our list of supported target frameworks is impressive, it does come with a cost. Some of these target frameworks are no longer supported by Microsoft and even required software that is no longer available for download.
We'd like to gather feedback from our users through this issue to learn how important it is to continue supporting all these target frameworks.
Do you want to keep track of general progress and annoucements for SSH.NET? You can now follow us on Twitter.
Do you or your company rely on SSH.NET in your projects? If you want to encourage us to keep on going and show us that you appreciate our work, please consider becoming a sponsor through GitHub Sponsors.
This release wouldn't have been possible without contributions from:
@darinkes, @AtosNicoS, @dancret, @menees, @ericbrumfield
Special thanks for @Filini producing our new logo assets!
Published by drieseng over 4 years ago
This release of SSH.NET adds support for .NET Standard 2.0.
This brings the full list of the supported target frameworks to:
Fixes issue #357, #436 and #474.
SSH.NET now supports the following additional key exchange algorithms:
Fixes issue #53, #406 and #504.
The following additional host key algorithms are now supported:
SSH.NET now supports the following private key formats:
Fixes issue #485.
A ChannelCloseTimeout property has been introduced on ConnectionInfo that controls the timeout to apply when waiting for a server to acknowledge closing a channel. The default value is 1 second.
If a server does not report to our SSH_MSG_CHANNEL_CLOSE message with a SSH_MSG_CHANNEL_CLOSE message before the specified timeout elapses, we'll stop waiting and consider the channel to be closed.
Fixes issue #335.
CVE-2018-20685 caused OpenSSH to implement more strict validation of file paths. ScpClient was updated to correctly deal with these changes.
In case of a recursive directory upload, ScpClient cannot fully absorb the impact of these changes and as such Upload(DirectoryInfo directoryInfo, string path)
now fails if path does not exist on the remote host. For the same reason, the mode and modification/access time will also no longer be set on path.
The mode and modification/access time continue to be set on any files or subdirectories that are uploaded.
Connect()
times out when server rejects initializing subsystem (issue #308).Disconnect()
and Dispose()
may block indefinitely on .NET Core (issue #355 and #686).While our list of supported target frameworks is impressive, it does come with a cost. Some of these target frameworks are no longer supported by Microsoft and even required software that is no longer available for download.
We'd like to gather feedback from our users through this issue to learn how important it is to continue supporting all these target frameworks.
Do you want to keep track of general progress and annoucements for SSH.NET? You can now follow us on Twitter.
Do you or your company rely on SSH.NET in your projects? If you want to encourage us to keep on going and show us that you appreciate our work, please consider becoming a sponsor through GitHub Sponsors.
This release wouldn't have been possible without contributions from:
@darinkes, @AtosNicoS, @dancret
Published by drieseng about 7 years ago
The NuGet Package Manager in Visual Studio 2012 does not support packages in which a given dependency is defined for more than one TFM. Since a fix for this issue is not expected any time soon, we're reviving support for a binary distribution.
This plain .zip file contains the SSH.NET assembly, and where applicable any dependencies, for all supported target frameworks.
For Visual Studio 2015 and higher, the SSH.NET NuGet package remains our primary distribution mechanism.
Disconnect()
and Dispose()
.A RemotePathTransformation property has been added to ScpClient that can be used to change if and how a remote path is transformed before it is passed to the scp command on the remote server.
SSH.NET comes with the following path transformations that are exposed through the RemotePathTransformation class (in Renci.SshNet):
DoubleQuote
Encloses a path in double quotes, and escapes any embedded double quote with a backslash.
This is the default, as it works fine with most SCP servers.
ShellQuote
Quotes a path in a way to be suitable to be used with a shell-based SCP server.
This is the preferred mechanism for shell-based SCP servers, which typically means all Unix-like systems.
None
Performs no transformation to the path.
This is the recommended transformation when the remote host does not require any quoting to preserve the literal value of metacharacters, or when remote paths are guaranteed not to contain such characters.
When none of the built-in transformations match the specific requirements of a remote host, a custom transformation can be supplied.
More information on this change is available here.
Fixes issues #256 and #108.
Example:
Download a file using SCP, and apply shell quoting to the remote path:
using Renci.SshNet;
public class Program
{
static void Main()
{
using (var client = new ScpClient("HOST", 22, "USER", "PWD"))
{
client.RemotePathTransformation = RemotePathTransformation.ShellQuote;
client.Connect();
using (var ms = new MemoryStream())
{
client.Download("/home/sshnet/file 123", ms);
}
}
}
}
Since we've configured ScpClient to use the ShellQuote transformation, the /home/sshnet/file 123
path
will automatically be enclosed in single quotes.
The actual path that is passed to the scp command on the remote host is therefore '/home/sshnet/file 123'
.
SftpFileStream.Read(byte[], int, int)
only writes those bytes to the buffer that go beyond the number of bytes requested by the caller.SftpFileStream.SetLength(long value)
with System.IO.FileStream (PR #272):
SftpClient.(Begin)DownloadFile(...)
by asynchronously reading ahead chunks (issue #145 and #100).The Upload(FileInfo fileInfo, String path)
method in ScpClient now expects path to be the remote path of the file to which you want to upload, and throws a ScpException if a directory exists on the remote host for that path (issue #286).
Up until now, uploading a file with a given (remote) path could lead a different result depending on whether a directory for that path exits on the remote host.
Example:
using (var client = new ScpClient("host", "user", "pwd"))
{
client.Connect();
client.Upload(new FileInfo(@"c:\temp\newlog.txt", "/home/sshnet/log");
}
As of version 2016.1.0-beta 3 a ScpException will be thrown when /home/sshnet/log exists as a directory on the remote host.
In previous versions of SSH.NET this code would actually upload the content of the c:\temp\newlog.txt file to /home/sshnet/log/newlog.txt.
When /home/sshnet/log exists on the remote host as a file or does not exist at all, the content of the c:\temp\newlog.txt file will be uploaded to /home/sshnet/log. This has not changed.
The IsAsync property was removed from SftpFileStream. Previously this property always returned false.
The read and write position in SftpFileStream are no longer tracked separately. Reading from or seeking in the SftpFileStream will now also affect the position at which a subsequent write is performed, and vice versa (issue #253).
For example, the following code snippet will now write "123456" to the console:
var buffer = Encoding.UTF8.GetBytes("123456");
using (var client = new SftpClient("host", "user", "pwd"))
{
client.Connect();
using (var ws = client.OpenWrite(path))
{
ws.Write(buffer, 0, 3);
}
using (var ws = client.OpenWrite(path))
{
ws.Seek(3, SeekOrigin.Begin);
ws.Write(buffer, 3, 3);
}
Console.WriteLine(client.ReadAllText(path, Encoding.UTF8));
}
To improve compatibility of SftpFileStream with System.IO.FileStream, Append mode is now only allowed when combined with write-only access (issue #267). This only affects SftpClient.Open(string path, FileMode mode, FileAccess access)
.
The following code snippet will now throw an ArgumentException:
using (var client = new SftpClient("host", "user", "pwd"))
{
client.Connect();
using (var fs = client.Open("/home/user/file.log", FileMode.Append, FileShare.Read))
{
}
}
Result:
System.ArgumentException: Append mode can be requested only when combined with write-only access.
To improve compatibility of SftpClient with System.IO.File, the following methods now use UTF-8 encoding without a Byte-Order Mark (BOM):
void AppendAllLines(string path, IEnumerable<string> contents)
void AppendAllText(string path, string contents)
StreamWriter AppendText(string path)
StreamWriter CreateText(string path)
void WriteAllLines(string path, IEnumerable<string> contents)
void WriteAllLines(string path, string[] contents)
void WriteAllText(string path, string contents)
Servers that do not implement RFC 4252 correctly may lead to stack overflow (issue #306).
Section 5.1 of the aforementioned RFC states the following on SSH_MSG_USERAUTH_FAILURE response:
The value of 'partial success' MUST be TRUE if the authentication request to which this is a response was successful. It MUST be FALSE if the request was not successfully processed.
Some SSH servers set 'partial success' to TRUE even if the authentication request was not processed successfully, and do not update the name-list to remove the method name that failed.
SSH.NET has now been updated only attempt authentication 5 times for a method that was considered partially successful.
If this limit is reached for a given method name, and no other authentication method is available, a SshAuthenticationException with message "Reached authentication attempt limit for method (<method name>)" will be thrown.
Key exchange is slow when size of group is more than 1024 bit (issue #304 and #130).
As part of the key exchange, SSH.NET sends a SSH_MSG_KEY_DH_GEX_REQUEST with a 1024 bit minimum and preferred group size, and 8192 bit maximum group size.
Before this fix, we would generate a private exponent that matches the size of the safe prime generated by the server. In some cases this meant using a 8192 bit private component to generate the client exchange value, which is a CPU intensive operation.
As from this fix, we generate a private exponent that is twice the hash size with a minimum of 1024 bit.
ConnectSocks5()
throws a ProxyException with a wrong message (issue #167).
Comments in ProxyException.cs are not relevant (issue #163).
SSH exception after client connect using .NET 3.5 version (issue #113).
Handle leak when connection to SSH server fails (issue #55).
Race condition when both server and client close channel (issue #84).
Suppressing a not-connected exception on socket shutdown (issue #86).
Race condition when both server and client disconnect (issue #80).
Do not consume request-specific data for SSH_MSG_GLOBAL_REQUEST (issue #58).
SocketAsyncEventArgs leak establishing socket connection (issue #133 and #87).
Upload(Stream source, String path)
does not support uploading a file to the home directory using a relative path (issue #280).
The following code will now just work:
using (var client = new ScpClient("host", "user", "pwd"))
{
client.Connect();
using (var fs = File.OpenRead(@"c:\file.log"))
{
client.Upload(fs, "file.log");
}
}
Upload(Stream source, String path)
does not throw an exception when parent directory does not exist (issue #289).
ScpClient does not support non-ASCII characters in downloaded file names or error message (issue #281).
DowloadFile
(String path, Stream output, Action<UInt64>)
does not perform well on Sun SSH (issue #292).SftpFileStream.ReadByte()
throws ArgumentException when no data is available in read buffer (issue #173).ShellStream.Write(byte[] buffer, Int32 offset, Int32 count)
skips a single byte when buffer is full (PR #211).CreateShellStream
overloads always use a 1024 byte buffer (issue #303)Dispose()
throws exception when SSH session gets disconnected unexpectedly (issue #96).ForwardedPort.Stop()
no longer throws ObjectDisposedException when the port is disposed.ScpClient.Upload(DirectoryInfo, string)
adds extra directory level (issue #128).ScpClient.Upload(DirectoryInfo, string)
applies timestamp of parent directory (issue #129).These release notes include all changes since 2016.0.0. The individual release notes for the releases leading up to the 2016.1.0 release are available here: beta 1, beta 2, beta 3, beta 4.
There have been no changes since beta 4.
Published by drieseng about 7 years ago
Servers that do not implement RFC 4252 correctly may lead to stack overflow (issue #306).
Section 5.1 of the aforementioned RFC states the following on SSH_MSG_USERAUTH_FAILURE response:
The value of 'partial success' MUST be TRUE if the authentication request to which this is a response was successful. It MUST be FALSE if the request was not successfully processed.
Some SSH servers set 'partial success' to TRUE even if the authentication request was not processed successfully, and do not update the name-list to remove the method name that failed.
SSH.NET has now been updated only attempt authentication 5 times for a method that was considered partially successful.
If this limit is reached for a given method name, and no other authentication method is available, a SshAuthenticationException with message "Reached authentication attempt limit for method (<method name>)" will be thrown.
Key exchange is slow when size of group is more than 1024 bit (issue #304 and #130).
As part of the key exchange, SSH.NET sends a SSH_MSG_KEY_DH_GEX_REQUEST with a 1024 bit minimum and preferred group size, and 8192 bit maximum group size.
Before this fix, we would generate a private exponent that matches the size of the safe prime generated by the server. In some cases this meant using a 8192 bit private component to generate the client exchange value, which is a CPU intensive operation.
As from this fix, we generate a private exponent that is twice the hash size with a minimum of 1024 bit.
Published by drieseng about 7 years ago
A RemotePathTransformation property has been added to ScpClient that can be used to change if and how a remote path is transformed before it is passed to the scp command on the remote server.
SSH.NET comes with the following path transformations that are exposed through the RemotePathTransformation class (in Renci.SshNet):
DoubleQuote
Encloses a path in double quotes, and escapes any embedded double quote with a backslash.
This is the default, as it works fine with most SCP servers.
ShellQuote
Quotes a path in a way to be suitable to be used with a shell-based SCP server.
This is the preferred mechanism for shell-based SCP servers, which typically means all Unix-like systems.
None
Performs no transformation to the path.
This is the recommended transformation when the remote host does not require any quoting to preserve the literal value of metacharacters, or when remote paths are guaranteed not to contain such characters.
When none of the built-in transformations match the specific requirements of a remote host, a custom transformation can be supplied.
More information on this change is available here.
Fixes issues #256 and #108.
Example:
Download a file using SCP, and apply shell quoting to the remote path:
using Renci.SshNet;
public class Program
{
static void Main()
{
using (var client = new ScpClient("HOST", 22, "USER", "PWD"))
{
client.RemotePathTransformation = RemotePathTransformation.ShellQuote;
client.Connect();
using (var ms = new MemoryStream())
{
client.Download("/home/sshnet/file 123", ms);
}
}
}
}
Since we've configured ScpClient to use the ShellQuote transformation, the /home/sshnet/file 123
path
will automatically be enclosed in single quotes.
The actual path that is passed to the scp command on the remote host is therefore '/home/sshnet/file 123'
.
The Upload(FileInfo fileInfo, String path)
method in ScpClient now expects path to be the remote path of the file to which you want to upload, and throws a ScpException if a directory exists on the remote host for that path (issue #286).
Up until now, uploading a file with a given (remote) path could lead a different result depending on whether a directory for that path exits on the remote host.
Example:
using (var client = new ScpClient("host", "user", "pwd"))
{
client.Connect();
client.Upload(new FileInfo(@"c:\temp\newlog.txt", "/home/sshnet/log");
}
As of version 2016.1.0-beta3 a ScpException will be thrown when /home/sshnet/log exists as a directory on the remote host.
In previous versions of SSH.NET this code would actually upload the content of the c:\temp\newlog.txt file to /home/sshnet/log/newlog.txt.
When /home/sshnet/log exists on the remote host as a file or does not exist at all, the content of the c:\temp\newlog.txt file will be uploaded to /home/sshnet/log. This has not changed.
Upload(Stream source, String path)
does not support uploading a file to the home directory using a relative path (issue #280).
The following code will now just work:
using (var client = new ScpClient("host", "user", "pwd"))
{
client.Connect();
using (var fs = File.OpenRead(@"c:\file.log"))
{
client.Upload(fs, "file.log");
}
}
Upload(Stream source, String path)
does not throw an exception when parent directory does not exist (issue #289).
ScpClient does not support non-ASCII characters in downloaded file names or error message (issue #281).
DowloadFile
(String path, Stream output, Action<UInt64>)
does not perform well on Sun SSH (issue #292).ShellStream.Write(byte[] buffer, Int32 offset, Int32 count)
skips a single byte when buffer is full (PR #211).CreateShellStream
overloads always use a 1024 byte buffer (issue #303)Published by drieseng about 7 years ago
SftpFileStream.Read(byte[], int, int)
only writes those bytes to the buffer that go beyond the number of bytes requested by the caller.SftpFileStream.SetLength(long value)
with System.IO.FileStream (PR #272):
SftpClient.(Begin)DownloadFile(...)
by asynchronously reading ahead chunks (issue #145 and #100).The IsAsync property was removed from SftpFileStream. Previously this property always returned false.
The read and write position in SftpFileStream are no longer tracked separately. Reading from or seeking in the SftpFileStream will now also affect the position at which a subsequent write is performed, and vice versa (issue #253).
For example, the following code snippet will now write "123456" to the console:
var buffer = Encoding.UTF8.GetBytes("123456");
using (var client = new SftpClient("host", "user", "pwd"))
{
client.Connect();
using (var ws = client.OpenWrite(path))
{
ws.Write(buffer, 0, 3);
}
using (var ws = client.OpenWrite(path))
{
ws.Seek(3, SeekOrigin.Begin);
ws.Write(buffer, 3, 3);
}
Console.WriteLine(client.ReadAllText(path, Encoding.UTF8));
}
To improve compatibility of SftpFileStream with System.IO.FileStream, Append mode is now only allowed when combined with write-only access (issue #267). This only affects SftpClient.Open(string path, FileMode mode, FileAccess access)
.
The following code snippet will now throw an ArgumentException:
using (var client = new SftpClient("host", "user", "pwd"))
{
client.Connect();
using (var fs = client.Open("/home/user/file.log", FileMode.Append, FileShare.Read))
{
}
}
Result:
System.ArgumentException: Append mode can be requested only when combined with write-only access.
To improve compatibility of SftpClient with System.IO.File, the following methods now use UTF-8 encoding without a Byte-Order Mark (BOM):
void AppendAllLines(string path, IEnumerable<string> contents)
void AppendAllText(string path, string contents)
StreamWriter AppendText(string path)
StreamWriter CreateText(string path)
void WriteAllLines(string path, IEnumerable<string> contents)
void WriteAllLines(string path, string[] contents)
void WriteAllText(string path, string contents)
ConnectSocks5()
throws a ProxyException with a wrong message (issue #167).SftpFileStream.ReadByte()
throws ArgumentException when no data is available in read buffer (issue #173).Published by drieseng almost 8 years ago
The NuGet Package Manager in Visual Studio 2012 does not support packages in which a given dependency is defined for more than one TFM. Since a fix for this issue is not expected any time soon, we're reviving support for a binary distribution.
This plain .zip file contains the SSH.NET assembly, and where applicable any dependencies, for all supported target frameworks.
For Visual Studio 2015 and higher, the SSH.NET NuGet package remains our primary distribution mechanism.
ForwardedPort.Stop()
no longer throws ObjectDisposedException when the port is disposed.Disconnect()
and Dispose()
.Dispose()
throws exception when SSH session gets disconnected unexpectedly (issue #96).ScpClient.Upload(DirectoryInfo, string)
adds extra directory level (issue #128).ScpClient.Upload(DirectoryInfo, string)
applies timestamp of parent directory (issue #129).Published by drieseng about 8 years ago
This is the first release of SSH.NET providing support for .NET Core and Universal Windows Platform 10 (UAP10.0). The support for .NET Core comes in the form of an assembly targeting the .NET Platform Standard 1.3 TFM.
The full list of the supported target frameworks for this release is:
Overall performance has been greatly improved. Some of the changes that contributed to these gains are:
The SSH.NET NuGet package is now our only binary distribution mechanism.
We no longer provide zip files that contain the assembly for individual target frameworks.
Up to now, stopping a forwarding port would block until all pending requests have completed and all channels are closed. This could result in a serious slowdown when stopping a SshClient with multiple forwarding ports.
As of this release, pending requests will be interrupted and the port will gracefully stop once all channels are closed.
Allround improvements to the API documentation.
Exceptions that are thrown during async processing are no longer wrapped in a SshException.
For example, the following code will now just work:
using (var client = new SftpClient(connectionInfo))
{
var asyncResult = client.BeginDownloadFile("/home/sshnet/remoteFileThatDoesNotExist", ms);
...
try
{
client.EndDownloadFile(asyncResult);
}
catch (SftpPathNotFoundException)
{
// we should be landing here
}
}
Published by drieseng about 8 years ago
This is the first release of SSH.NET providing support for .NET Core.
The focus for this release was feature completeness and stability.
Future releases will focus on performance.
Up to now, stopping a forwarding port would block until all pending requests have completed and all channels are closed. This could result in a serious slowdown when stopping a SshClient with multiple forwarding ports.
As of this release, pending requests will be interrupted and the port will gracefully stop once all channels are closed.
Allround improvements to the API documentation.
Exceptions that are thrown during async processing are no longer wrapped in a SshException.
For example, the following code will now just work:
using (var client = new SftpClient(connectionInfo))
{
var asyncResult = client.BeginDownloadFile("/home/sshnet/remoteFileThatDoesNotExist", ms);
...
try
{
client.EndDownloadFile(asyncResult);
}
catch (SftpPathNotFoundException)
{
// we should be landing here
}
}
Published by drieseng over 8 years ago
Published by drieseng over 8 years ago