diff --git a/.editorconfig b/.editorconfig
index 18f2172f8..944a6bf07 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -77,6 +77,12 @@ dotnet_diagnostic.S1172.severity = none
# This is a duplicate of IDE0059.
dotnet_diagnostic.S1481.severity = none
+# S1854: Unused assignments should be removed
+# https://rules.sonarsource.com/csharp/RSPEC-1854
+#
+# This is a duplicate of IDE0059.
+dotnet_diagnostic.S1854.severity = none
+
# S2259: Null pointers should not be dereferenced
# https://rules.sonarsource.com/csharp/RSPEC-2259
#
@@ -168,6 +174,12 @@ dotnet_diagnostic.S3928.severity = none
# This is a duplicate of CA2002, and partial duplicate of MA0064.
dotnet_diagnostic.S3998.severity = none
+# S4070: Non-flags enums should not be marked with "FlagsAttribute"
+# https://rules.sonarsource.com/csharp/RSPEC-4070
+#
+# This is a duplicate of MA0062.
+dotnet_diagnostic.S4070.severity = none
+
# S4456: Parameter validation in yielding methods should be wrapped
# https://rules.sonarsource.com/csharp/RSPEC-4456
#
@@ -364,6 +376,14 @@ dotnet_diagnostic.MA0018.severity = none
# No strong need for this, and may negatively affect performance.
dotnet_diagnostic.MA0021.severity = none
+# MA0025: Implement the functionality instead of throwing NotImplementedException
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0031.md
+dotnet_diagnostic.MA0025.severity = none
+
+# MA0026: Fix TODO comment
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0026.md
+dotnet_diagnostic.MA0026.severity = suggestion
+
# MA0031: Optimize Enumerable.Count() usage
# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0031.md
#
@@ -436,6 +456,12 @@ dotnet_diagnostic.MA0112.severity = error
# instead of a base class or interface.
dotnet_diagnostic.CA1002.severity = none
+# CA1003: Use generic event handler instances
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1003
+#
+# Similar to MA0046.
+dotnet_diagnostic.CA1003.severity = none
+
# CA1008: Enums should have zero value
#
# TODO: To be discussed. Having a zero value offers a performance advantage.
@@ -460,6 +486,10 @@ dotnet_diagnostic.CA1051.severity = none
# By default, this diagnostic is only reported for public types.
dotnet_code_quality.CA1052.api_surface = all
+# CA1065: Do not raise exceptions in unexpected locations
+# https://learn.microsoft.com/en-US/dotnet/fundamentals/code-analysis/quality-rules/ca1065
+dotnet_diagnostic.CA1065.severity = none
+
# CA1303: Do not pass literals as localized parameters
#
# We don't care about localization.
@@ -473,6 +503,10 @@ dotnet_diagnostic.CA1303.severity = none
# Submitted https://github.com/dotnet/roslyn-analyzers/issues/6096 to fix CA1305.
dotnet_diagnostic.CA1305.severity = none
+# CA1309: Use ordinal StringComparison
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1309
+dotnet_diagnostic.CA1309.severity = none
+
# CA1510: Use ArgumentNullException throw helper
#
# This is only available in .NET 6.0 and higher. We'd need to use conditional compilation to only
@@ -577,6 +611,22 @@ dotnet_diagnostic.IDE0130.severity = none
# var inputPath = originalDossierPathList.Find(x => x.id == updatedPath.id) ?? throw new PcsException($"Path id ({updatedPath.id}) unknown in PCS for dossier id {dossierFromTs.dossier.id}", updatedPath.id);
dotnet_diagnostic.IDE0270.severity = none
+# IDE0290: Use primary constructor
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0290
+dotnet_diagnostic.IDE0290.severity = none
+
+# IDE0300: Collection initialization can be simplified
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300
+#
+# TODO: Discuss whether we want to start using this
+dotnet_diagnostic.IDE0300.severity = none
+
+# IDE0301: Simplify collection initialization
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0301
+#
+# TODO: Discuss whether we want to start using this
+dotnet_diagnostic.IDE0301.severity = none
+
#### .NET Compiler Platform code style rules ####
### Language rules ###
diff --git a/Directory.Build.props b/Directory.Build.props
index 100efff95..4ffd15e32 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -18,7 +18,7 @@
Code analysis properties.
-->
- false
+ true
preview-All
true
@@ -34,11 +34,9 @@
Use fixed version of analyzers.
-->
-
+
+
diff --git a/src/Renci.SshNet/.editorconfig b/src/Renci.SshNet/.editorconfig
index 97f5eb12b..c5f4bdf61 100644
--- a/src/Renci.SshNet/.editorconfig
+++ b/src/Renci.SshNet/.editorconfig
@@ -1,5 +1,38 @@
[*.cs]
+#### Sonar rules ####
+
+# S1264: A "while" loop should be used instead of a "for" loop
+# https://rules.sonarsource.com/csharp/RSPEC-1264
+dotnet_diagnostic.S1264.severity = none
+
+# S1450: Private fields only used as local variables in methods should become local variables
+# https://rules.sonarsource.com/csharp/RSPEC-1450
+#
+# TODO: Re-enable when the following issue is resolved:
+# https://github.com/SonarSource/sonar-dotnet/issues/8239
+dotnet_diagnostic.S1450.severity = none
+
+# S2372: Exceptions should not be thrown from property getters
+# https://rules.sonarsource.com/csharp/RSPEC-2372/
+dotnet_diagnostic.S2372.severity = none
+
+# S2583: Conditionally executed code should be reachable
+# https://rules.sonarsource.com/csharp/RSPEC-2583/
+#
+# TODO: Re-enable when the following issue is resolved:
+# https://github.com/SonarSource/sonar-dotnet/issues/8264
+dotnet_diagnostic.S2583.severity = none
+
+# S2589: Boolean expressions should not be gratuitous
+# https://rules.sonarsource.com/csharp/RSPEC-2589/
+#
+# TODO: Re-enable when the following issue is resolved:
+# https://github.com/SonarSource/sonar-dotnet/issues/8262
+dotnet_diagnostic.S2589.severity = none
+
+dotnet_diagnostic.S2372.severity = none
+
#### SYSLIB diagnostics ####
# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time
@@ -7,32 +40,114 @@
# TODO: Remove this when https://github.com/sshnet/SSH.NET/issues/1131 is implemented.
dotnet_diagnostic.SYSLIB1045.severity = none
-### StyleCop Analyzers rules ###
+#### StyleCop Analyzers rules ####
+
+# SA1123: Do not place regions within elements
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1123.md
+dotnet_diagnostic.SA1123.severity = none
+
+# SA1124: Do not use regions
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1124.md
+dotnet_diagnostic.SA1124.severity = none
# SA1202: Elements must be ordered by access
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1202.md
dotnet_diagnostic.SA1202.severity = none
+# SA1204: Static elements must appear before instance elements
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1204.md
+dotnet_diagnostic.SA1204.severity = none
+
+# SA1310: Field names must not contain underscore
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1310.md
+#dotnet_diagnostic.SA1310.severity = none
+
+# SA1312: Variable names should begin with lower-case letter
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
+dotnet_diagnostic.SA1312.severity = none
+
+# SA1636: File header copyright text should match
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1636.md
+dotnet_diagnostic.SA1636.severity = none
+
+# SA1643: Destructor summary documentation must begin with standard text
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1643.md
+dotnet_diagnostic.SA1643.severity = none
+
#### Meziantou.Analyzer rules ####
+# MA0001: StringComparison is missing
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0001.md
+dotnet_diagnostic.MA0001.severity = none
+
+# MA0011: IFormatProvider is missing
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0011.md
+#
+# TODO: Remove exclusion when issues are fixed
+dotnet_diagnostic.MA0011.severity = none
+
+# MA0015: Specify the parameter name in ArgumentException
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0015.md
+#
+# TODO: Remove exclusion when issues are fixed
+dotnet_diagnostic.MA0015.severity = none
+
+# MA0050: Validate arguments correctly in iterator methods
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0050.md
+#
+# TODO: Re-enable when https://github.com/meziantou/Meziantou.Analyzer/issues/617 is fixed
+dotnet_diagnostic.MA0050.severity = none
+
# MA0053: Make class sealed
# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md
MA0053.public_class_should_be_sealed = false
+# MA0055: Do not use finalizer
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0055.md
+#
+# TODO: Remove exclusion when issues are fixed
+dotnet_diagnostic.MA0055.severity = none
+
+# MA0110: Use the Regex source generator
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0110.md
+dotnet_diagnostic.MA0110.severity = none
+
#### .NET Compiler Platform analysers rules ####
# CA1030: Use events where appropriate
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1030
-dotnet_diagnostic.CA10310.severity = none
+dotnet_diagnostic.CA1030.severity = none
# CA1031: Do not catch general exception types
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1031
dotnet_diagnostic.CA1031.severity = none
+# CA1062: Validate arguments of public methods
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1062
+#
+# TODO: Remove exclusion when issues are fixed
+dotnet_diagnostic.CA1062.severity = none
+
+# CA1307: Specify StringComparison for clarity
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1307
+dotnet_diagnostic.CA1307.severity = none
+
+# CA1716: Identifiers should not match keywords
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1716
+dotnet_diagnostic.CA1716.severity = none
+
+# CA1822: Mark members as static
+# https://learn.microsoft.com/en-US/dotnet/fundamentals/code-analysis/quality-rules/ca1822
+dotnet_code_quality.CA1822.api_surface = private,internal
+
# CA2213: Disposable fields should be disposed
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2213
dotnet_diagnostic.CA2213.severity = none
+# CA3075: Insecure DTD Processing
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca3075
+dotnet_diagnostic.CA3075.severity = none
+
# IDE0004: Types that own disposable fields should be disposable
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0004
dotnet_diagnostic.IDE0004.severity = none
@@ -40,3 +155,7 @@ dotnet_diagnostic.IDE0004.severity = none
# IDE0048: Add parentheses for clarity
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0047
dotnet_diagnostic.IDE0048.severity = none
+
+# IDE0305: Collection initialization can be simplified
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0305
+dotnet_diagnostic.IDE0305.severity = none
diff --git a/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs b/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs
index 18275e724..014d70689 100644
--- a/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs
+++ b/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs
@@ -22,11 +22,7 @@ public static bool IsEnabled(TraceEventType traceEventType)
public static void Log(string text)
{
Loggging.TraceEvent(TraceEventType.Verbose,
-#if NET6_0_OR_GREATER
System.Environment.CurrentManagedThreadId,
-#else
- System.Threading.Thread.CurrentThread.ManagedThreadId,
-#endif // NET6_0_OR_GREATER
text);
}
}
diff --git a/src/Renci.SshNet/Abstractions/DnsAbstraction.cs b/src/Renci.SshNet/Abstractions/DnsAbstraction.cs
index 0a3f98ecc..30a10fe87 100644
--- a/src/Renci.SshNet/Abstractions/DnsAbstraction.cs
+++ b/src/Renci.SshNet/Abstractions/DnsAbstraction.cs
@@ -25,7 +25,7 @@ internal static class DnsAbstraction
///
/// Returns the Internet Protocol (IP) addresses for the specified host.
///
- /// The host name or IP address to resolve
+ /// The host name or IP address to resolve.
///
/// An array of type that holds the IP addresses for the host that
/// is specified by the parameter.
@@ -34,7 +34,7 @@ internal static class DnsAbstraction
/// An error is encountered when resolving .
public static IPAddress[] GetHostAddresses(string hostNameOrAddress)
{
- // TODO Eliminate sync variant, and implement timeout
+ /* TODO Eliminate sync variant, and implement timeout */
#if FEATURE_DNS_SYNC
return Dns.GetHostAddresses(hostNameOrAddress);
@@ -92,7 +92,7 @@ public static IPAddress[] GetHostAddresses(string hostNameOrAddress)
///
/// Returns the Internet Protocol (IP) addresses for the specified host.
///
- /// The host name or IP address to resolve
+ /// The host name or IP address to resolve.
///
/// A task with result of an array of type that holds the IP addresses for the host that
/// is specified by the parameter.
diff --git a/src/Renci.SshNet/Abstractions/ReflectionAbstraction.cs b/src/Renci.SshNet/Abstractions/ReflectionAbstraction.cs
index 1140c9a60..d9e15a508 100644
--- a/src/Renci.SshNet/Abstractions/ReflectionAbstraction.cs
+++ b/src/Renci.SshNet/Abstractions/ReflectionAbstraction.cs
@@ -7,7 +7,7 @@ namespace Renci.SshNet.Abstractions
internal static class ReflectionAbstraction
{
public static IEnumerable GetCustomAttributes(this Type type, bool inherit)
- where T:Attribute
+ where T : Attribute
{
var attributes = type.GetCustomAttributes(typeof(T), inherit);
return attributes.Cast();
diff --git a/src/Renci.SshNet/Abstractions/SocketAbstraction.cs b/src/Renci.SshNet/Abstractions/SocketAbstraction.cs
index f325fdc30..f8b289168 100644
--- a/src/Renci.SshNet/Abstractions/SocketAbstraction.cs
+++ b/src/Renci.SshNet/Abstractions/SocketAbstraction.cs
@@ -261,11 +261,6 @@ public static byte[] Read(Socket socket, int size, TimeSpan timeout)
return buffer;
}
- public static Task ReadAsync(Socket socket, byte[] buffer, int offset, int length, CancellationToken cancellationToken)
- {
- return socket.ReceiveAsync(buffer, offset, length, cancellationToken);
- }
-
///
/// Receives data from a bound into a receive buffer.
///
@@ -293,7 +288,7 @@ public static int Read(Socket socket, byte[] buffer, int offset, int size, TimeS
var totalBytesRead = 0;
var totalBytesToRead = size;
- socket.ReceiveTimeout = (int)readTimeout.TotalMilliseconds;
+ socket.ReceiveTimeout = (int) readTimeout.TotalMilliseconds;
do
{
@@ -330,6 +325,11 @@ public static int Read(Socket socket, byte[] buffer, int offset, int size, TimeS
return totalBytesRead;
}
+ public static Task ReadAsync(Socket socket, byte[] buffer, int offset, int length, CancellationToken cancellationToken)
+ {
+ return socket.ReceiveAsync(buffer, offset, length, cancellationToken);
+ }
+
public static void Send(Socket socket, byte[] data)
{
Send(socket, data, 0, data.Length);
diff --git a/src/Renci.SshNet/Abstractions/SocketExtensions.cs b/src/Renci.SshNet/Abstractions/SocketExtensions.cs
index a51d0cb8d..9edfbf94b 100644
--- a/src/Renci.SshNet/Abstractions/SocketExtensions.cs
+++ b/src/Renci.SshNet/Abstractions/SocketExtensions.cs
@@ -10,24 +10,25 @@ namespace Renci.SshNet.Abstractions
// Async helpers based on https://devblogs.microsoft.com/pfxteam/awaiting-socket-operations/
internal static class SocketExtensions
{
- private sealed class SocketAsyncEventArgsAwaitable : SocketAsyncEventArgs, INotifyCompletion
+ private sealed class AwaitableSocketAsyncEventArgs : SocketAsyncEventArgs, INotifyCompletion
{
private static readonly Action SENTINEL = () => { };
private bool _isCancelled;
private Action _continuationAction;
- public SocketAsyncEventArgsAwaitable()
+ public AwaitableSocketAsyncEventArgs()
{
- Completed += delegate { SetCompleted(); };
+ Completed += (sender, e) => SetCompleted();
}
- public SocketAsyncEventArgsAwaitable ExecuteAsync(Func func)
+ public AwaitableSocketAsyncEventArgs ExecuteAsync(Func func)
{
if (!func(this))
{
SetCompleted();
}
+
return this;
}
@@ -48,7 +49,9 @@ public void SetCancelled()
SetCompleted();
}
- public SocketAsyncEventArgsAwaitable GetAwaiter()
+#pragma warning disable S1144 // Unused private types or members should be removed
+ public AwaitableSocketAsyncEventArgs GetAwaiter()
+#pragma warning restore S1144 // Unused private types or members should be removed
{
return this;
}
@@ -64,7 +67,9 @@ void INotifyCompletion.OnCompleted(Action continuation)
}
}
+#pragma warning disable S1144 // Unused private types or members should be removed
public void GetResult()
+#pragma warning restore S1144 // Unused private types or members should be removed
{
if (_isCancelled)
{
@@ -88,11 +93,15 @@ public static async Task ConnectAsync(this Socket socket, IPEndPoint remoteEndpo
{
cancellationToken.ThrowIfCancellationRequested();
- using (var args = new SocketAsyncEventArgsAwaitable())
+ using (var args = new AwaitableSocketAsyncEventArgs())
{
args.RemoteEndPoint = remoteEndpoint;
- using (cancellationToken.Register(o => ((SocketAsyncEventArgsAwaitable)o).SetCancelled(), args, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(o => ((AwaitableSocketAsyncEventArgs)o).SetCancelled(), args, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(o => ((AwaitableSocketAsyncEventArgs) o).SetCancelled(), args, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
await args.ExecuteAsync(socket.ConnectAsync);
}
@@ -103,11 +112,15 @@ public static async Task ReceiveAsync(this Socket socket, byte[] buffer, in
{
cancellationToken.ThrowIfCancellationRequested();
- using (var args = new SocketAsyncEventArgsAwaitable())
+ using (var args = new AwaitableSocketAsyncEventArgs())
{
args.SetBuffer(buffer, offset, length);
- using (cancellationToken.Register(o => ((SocketAsyncEventArgsAwaitable)o).SetCancelled(), args, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(o => ((AwaitableSocketAsyncEventArgs) o).SetCancelled(), args, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(o => ((AwaitableSocketAsyncEventArgs) o).SetCancelled(), args, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
await args.ExecuteAsync(socket.ReceiveAsync);
}
diff --git a/src/Renci.SshNet/Abstractions/ThreadAbstraction.cs b/src/Renci.SshNet/Abstractions/ThreadAbstraction.cs
index 0c9e3b64f..b3fe2bb4f 100644
--- a/src/Renci.SshNet/Abstractions/ThreadAbstraction.cs
+++ b/src/Renci.SshNet/Abstractions/ThreadAbstraction.cs
@@ -1,4 +1,6 @@
using System;
+using System.Threading;
+using System.Threading.Tasks;
namespace Renci.SshNet.Abstractions
{
@@ -10,18 +12,28 @@ internal static class ThreadAbstraction
/// The number of milliseconds for which the thread is suspended.
public static void Sleep(int millisecondsTimeout)
{
- System.Threading.Thread.Sleep(millisecondsTimeout);
+ Thread.Sleep(millisecondsTimeout);
}
- public static void ExecuteThreadLongRunning(Action action)
+ ///
+ /// Creates and starts a long-running for the specified .
+ ///
+ /// The to start.
+ /// is .
+ ///
+ /// A task that represents the execution of the specified .
+ ///
+ public static Task ExecuteThreadLongRunning(Action action)
{
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}
- var taskCreationOptions = System.Threading.Tasks.TaskCreationOptions.LongRunning;
- _ = System.Threading.Tasks.Task.Factory.StartNew(action, taskCreationOptions);
+ return Task.Factory.StartNew(action,
+ CancellationToken.None,
+ TaskCreationOptions.LongRunning,
+ TaskScheduler.Current);
}
///
@@ -35,7 +47,7 @@ public static void ExecuteThread(Action action)
throw new ArgumentNullException(nameof(action));
}
- _ = System.Threading.ThreadPool.QueueUserWorkItem(o => action());
+ _ = ThreadPool.QueueUserWorkItem(o => action());
}
}
}
diff --git a/src/Renci.SshNet/AuthenticationMethod.cs b/src/Renci.SshNet/AuthenticationMethod.cs
index b7ad7b682..16783f1bd 100644
--- a/src/Renci.SshNet/AuthenticationMethod.cs
+++ b/src/Renci.SshNet/AuthenticationMethod.cs
@@ -13,7 +13,9 @@ public abstract class AuthenticationMethod : IAuthenticationMethod
///
/// The name of the authentication method.
///
+#pragma warning disable CA2119 // Seal methods that satisfy private interfaces
public abstract string Name { get; }
+#pragma warning restore CA2119 // Seal methods that satisfy private interfaces
///
/// Gets connection username.
diff --git a/src/Renci.SshNet/AuthenticationResult.cs b/src/Renci.SshNet/AuthenticationResult.cs
index 714149669..9dc54648f 100644
--- a/src/Renci.SshNet/AuthenticationResult.cs
+++ b/src/Renci.SshNet/AuthenticationResult.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet
{
///
- /// Represents possible authentication methods results
+ /// Represents possible authentication methods results.
///
public enum AuthenticationResult
{
@@ -9,10 +9,12 @@ public enum AuthenticationResult
/// Authentication was successful.
///
Success,
+
///
/// Authentication completed with partial success.
///
PartialSuccess,
+
///
/// Authentication failed.
///
diff --git a/src/Renci.SshNet/BaseClient.cs b/src/Renci.SshNet/BaseClient.cs
index ca91a90f6..ddd434c2e 100644
--- a/src/Renci.SshNet/BaseClient.cs
+++ b/src/Renci.SshNet/BaseClient.cs
@@ -12,7 +12,7 @@ namespace Renci.SshNet
///
/// Serves as base class for client implementations, provides common client functionality.
///
- public abstract class BaseClient : IBaseClient, IDisposable
+ public abstract class BaseClient : IBaseClient
{
///
/// Holds value indicating whether the connection info is owned by this client.
@@ -341,7 +341,9 @@ public void Disconnect()
/// intervals.
///
/// The method was called after the client was disposed.
+#pragma warning disable S1133 // Deprecated code should be removed
[Obsolete("Use KeepAliveInterval to send a keep-alive message at regular intervals.")]
+#pragma warning restore S1133 // Deprecated code should be removed
public void SendKeepAlive()
{
CheckDisposed();
@@ -393,8 +395,6 @@ private void Session_HostKeyReceived(object sender, HostKeyEventArgs e)
///
public void Dispose()
{
- DiagnosticAbstraction.Log("Disposing client.");
-
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
@@ -412,14 +412,17 @@ protected virtual void Dispose(bool disposing)
if (disposing)
{
+ DiagnosticAbstraction.Log("Disposing client.");
+
Disconnect();
- if (_ownsConnectionInfo && _connectionInfo != null)
+ if (_ownsConnectionInfo && _connectionInfo is not null)
{
if (_connectionInfo is IDisposable connectionInfoDisposable)
{
connectionInfoDisposable.Dispose();
}
+
_connectionInfo = null;
}
@@ -433,10 +436,14 @@ protected virtual void Dispose(bool disposing)
/// THe current instance is disposed.
protected void CheckDisposed()
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
if (_isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
+#endif // NET7_0_OR_GREATER
}
///
diff --git a/src/Renci.SshNet/Channels/Channel.cs b/src/Renci.SshNet/Channels/Channel.cs
index f68a39479..580c63e0f 100644
--- a/src/Renci.SshNet/Channels/Channel.cs
+++ b/src/Renci.SshNet/Channels/Channel.cs
@@ -16,13 +16,14 @@ namespace Renci.SshNet.Channels
internal abstract class Channel : IChannel
{
private readonly object _serverWindowSizeLock = new object();
+ private readonly object _messagingLock = new object();
private readonly uint _initialWindowSize;
+ private readonly ISession _session;
private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(initialState: false);
private EventWaitHandle _channelServerWindowAdjustWaitHandle = new ManualResetEvent(initialState: false);
private uint? _remoteWindowSize;
private uint? _remoteChannelNumber;
private uint? _remotePacketSize;
- private readonly ISession _session;
private bool _isDisposed;
///
@@ -497,7 +498,7 @@ public void SendEof()
throw CreateChannelClosedException();
}
- lock (this)
+ lock (_messagingLock)
{
_session.SendMessage(new ChannelEofMessage(RemoteChannelNumber));
_eofMessageSent = true;
@@ -525,7 +526,7 @@ protected virtual void Close()
* message causing the server to disconnect the session.
*/
- lock (this)
+ lock (_messagingLock)
{
// Send EOF message first the following conditions are met:
// * we have not sent a SSH_MSG_CHANNEL_EOF message
@@ -704,7 +705,6 @@ private void OnChannelRequest(object sender, MessageEventArgs
- /// Initializes a new instance.
+ /// Initializes a new instance of the class.
///
/// The session.
/// The local channel number.
@@ -69,17 +69,17 @@ public void Bind(IPEndPoint remoteEndpoint, IForwardedPort forwardedPort)
_forwardedPort = forwardedPort;
_forwardedPort.Closing += ForwardedPort_Closing;
- // Try to connect to the socket
+ // Try to connect to the socket
try
{
_socket = SocketAbstraction.Connect(remoteEndpoint, ConnectionInfo.Timeout);
- // send channel open confirmation message
+ // Send channel open confirmation message
SendMessage(new ChannelOpenConfirmationMessage(RemoteChannelNumber, LocalWindowSize, LocalPacketSize, LocalChannelNumber));
}
catch (Exception exp)
{
- // send channel open failure message
+ // Send channel open failure message
SendMessage(new ChannelOpenFailureMessage(RemoteChannelNumber, exp.ToString(), ChannelOpenFailureMessage.ConnectFailed, "en"));
throw;
diff --git a/src/Renci.SshNet/Channels/ChannelSession.cs b/src/Renci.SshNet/Channels/ChannelSession.cs
index c70e9d317..f222fc647 100644
--- a/src/Renci.SshNet/Channels/ChannelSession.cs
+++ b/src/Renci.SshNet/Channels/ChannelSession.cs
@@ -394,7 +394,7 @@ protected override void OnFailure()
///
private void SendChannelOpenMessage()
{
- // do not allow open to be ChannelOpenMessage to be sent again until we've
+ // do not allow the ChannelOpenMessage to be sent again until we've
// had a response on the previous attempt for the current channel
if (Interlocked.CompareExchange(ref _sessionSemaphoreObtained, 1, 0) == 0)
{
diff --git a/src/Renci.SshNet/Channels/ChannelTypes.cs b/src/Renci.SshNet/Channels/ChannelTypes.cs
index 230420232..cd5e0ed0a 100644
--- a/src/Renci.SshNet/Channels/ChannelTypes.cs
+++ b/src/Renci.SshNet/Channels/ChannelTypes.cs
@@ -1,5 +1,4 @@
-
-namespace Renci.SshNet.Channels
+namespace Renci.SshNet.Channels
{
///
/// Lists channel types as defined by the protocol.
@@ -7,19 +6,22 @@ namespace Renci.SshNet.Channels
internal enum ChannelTypes
{
///
- /// session
+ /// Session.
///
Session,
+
///
- /// x11
+ /// X11.
///
X11,
+
///
- /// forwarded-tcpip
+ /// Forwarded-tcpip.
///
ForwardedTcpip,
+
///
- /// direct-tcpip
+ /// Direct-tcpip.
///
DirectTcpip
}
diff --git a/src/Renci.SshNet/Channels/ClientChannel.cs b/src/Renci.SshNet/Channels/ClientChannel.cs
index 3a0d05e68..ea4bfe164 100644
--- a/src/Renci.SshNet/Channels/ClientChannel.cs
+++ b/src/Renci.SshNet/Channels/ClientChannel.cs
@@ -49,7 +49,7 @@ protected virtual void OnOpenConfirmation(uint remoteChannelNumber, uint initial
///
/// Send message to open a channel.
///
- /// Message to send
+ /// Message to send.
/// The client is not connected.
/// The operation timed out.
/// The size of the packet exceeds the maximum size defined by the protocol.
diff --git a/src/Renci.SshNet/Channels/IChannelSession.cs b/src/Renci.SshNet/Channels/IChannelSession.cs
index 4b0bdd202..13c2e2304 100644
--- a/src/Renci.SshNet/Channels/IChannelSession.cs
+++ b/src/Renci.SshNet/Channels/IChannelSession.cs
@@ -25,8 +25,12 @@ internal interface IChannelSession : IChannel
///
/// if request was successful; otherwise .
///
- bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height,
- IDictionary terminalModeValues);
+ bool SendPseudoTerminalRequest(string environmentVariable,
+ uint columns,
+ uint rows,
+ uint width,
+ uint height,
+ IDictionary terminalModeValues);
///
/// Sends the X11 forwarding request.
diff --git a/src/Renci.SshNet/CipherInfo.cs b/src/Renci.SshNet/CipherInfo.cs
index 81e5e0689..2c9832a19 100644
--- a/src/Renci.SshNet/CipherInfo.cs
+++ b/src/Renci.SshNet/CipherInfo.cs
@@ -5,7 +5,7 @@
namespace Renci.SshNet
{
///
- /// Holds information about key size and cipher to use
+ /// Holds information about key size and cipher to use.
///
public class CipherInfo
{
diff --git a/src/Renci.SshNet/ClientAuthentication.cs b/src/Renci.SshNet/ClientAuthentication.cs
index 1cdfbecbe..5de87e536 100644
--- a/src/Renci.SshNet/ClientAuthentication.cs
+++ b/src/Renci.SshNet/ClientAuthentication.cs
@@ -1,9 +1,14 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
+
using Renci.SshNet.Common;
namespace Renci.SshNet
{
+ ///
+ /// Represents a mechanism to authenticate a given client.
+ ///
internal sealed class ClientAuthentication : IClientAuthentication
{
private readonly int _partialSuccessLimit;
@@ -37,11 +42,14 @@ internal int PartialSuccessLimit
}
///
- /// Attempts to authentication for a given using the
- /// of the specified .
+ /// Attempts to perform authentication for a given using the
+ /// of the specified
+ /// .
///
/// A to use for authenticating.
/// The for which to perform authentication.
+ /// or is .
+ /// Failed to authenticate the client.
public void Authenticate(IConnectionInfoInternal connectionInfo, ISession session)
{
if (connectionInfo is null)
@@ -102,8 +110,14 @@ private bool TryAuthenticate(ISession session,
var matchingAuthenticationMethods = authenticationState.GetSupportedAuthenticationMethods(allowedAuthenticationMethods);
if (matchingAuthenticationMethods.Count == 0)
{
- authenticationException = new SshAuthenticationException(string.Format("No suitable authentication method found to complete authentication ({0}).",
- string.Join(",", allowedAuthenticationMethods)));
+ authenticationException = new SshAuthenticationException(string.Format(CultureInfo.InvariantCulture,
+ "No suitable authentication method found to complete authentication ({0}).",
+#if NET || NETSTANDARD2_1_OR_GREATER
+ string.Join(',', allowedAuthenticationMethods)))
+#else
+ string.Join(",", allowedAuthenticationMethods)))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
+ ;
return false;
}
@@ -113,7 +127,7 @@ private bool TryAuthenticate(ISession session,
// methods after a partial success
if (authenticationState.GetPartialSuccessCount(authenticationMethod) >= _partialSuccessLimit)
{
- // TODO Get list of all authentication methods that have reached the partial success limit?
+ /* TODO Get list of all authentication methods that have reached the partial success limit? */
authenticationException = new SshAuthenticationException(string.Format("Reached authentication attempt limit for method ({0}).",
authenticationMethod.Name));
@@ -195,7 +209,7 @@ public void RecordPartialSuccess(IAuthenticationMethod authenticationMethod)
{
if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out var partialSuccessCount))
{
- _authenticationMethodPartialSuccessRegister[authenticationMethod] = ++partialSuccessCount;
+ _authenticationMethodPartialSuccessRegister[authenticationMethod] = partialSuccessCount + 1;
}
else
{
diff --git a/src/Renci.SshNet/Common/AsyncResult.cs b/src/Renci.SshNet/Common/AsyncResult.cs
index 59b71bb48..5f62b1b4a 100644
--- a/src/Renci.SshNet/Common/AsyncResult.cs
+++ b/src/Renci.SshNet/Common/AsyncResult.cs
@@ -155,50 +155,4 @@ public bool IsCompleted
get { return _completedState != StatePending; }
}
}
-
- ///
- /// Base class to encapsulates the results of an asynchronous operation that returns result.
- ///
- /// The type of the result.
- public abstract class AsyncResult : AsyncResult
- {
- // Field set when operation completes
- private TResult _result;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The async callback.
- /// The state.
- protected AsyncResult(AsyncCallback asyncCallback, object state)
- : base(asyncCallback, state)
- {
- }
-
- ///
- /// Marks asynchronous operation as completed.
- ///
- /// The result.
- /// if set to [completed synchronously].
- public void SetAsCompleted(TResult result, bool completedSynchronously)
- {
- // Save the asynchronous operation's result
- _result = result;
-
- // Tell the base class that the operation completed successfully (no exception)
- SetAsCompleted(exception: null, completedSynchronously);
- }
-
- ///
- /// Waits until the asynchronous operation completes, and then returns the value generated by the asynchronous operation.
- ///
- ///
- /// The invocation result.
- ///
- public new TResult EndInvoke()
- {
- base.EndInvoke(); // Wait until operation has completed
- return _result; // Return the result (if above didn't throw)
- }
- }
}
diff --git a/src/Renci.SshNet/Common/AsyncResult{TResult}.cs b/src/Renci.SshNet/Common/AsyncResult{TResult}.cs
new file mode 100644
index 000000000..b5ab54122
--- /dev/null
+++ b/src/Renci.SshNet/Common/AsyncResult{TResult}.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace Renci.SshNet.Common
+{
+ ///
+ /// Base class to encapsulates the results of an asynchronous operation that returns result.
+ ///
+ /// The type of the result.
+ public abstract class AsyncResult : AsyncResult
+ {
+ // Field set when operation completes
+ private TResult _result;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The async callback.
+ /// The state.
+ protected AsyncResult(AsyncCallback asyncCallback, object state)
+ : base(asyncCallback, state)
+ {
+ }
+
+ ///
+ /// Marks asynchronous operation as completed.
+ ///
+ /// The result.
+ /// if set to [completed synchronously].
+ public void SetAsCompleted(TResult result, bool completedSynchronously)
+ {
+ // Save the asynchronous operation's result
+ _result = result;
+
+ // Tell the base class that the operation completed successfully (no exception)
+ SetAsCompleted(exception: null, completedSynchronously);
+ }
+
+ ///
+ /// Waits until the asynchronous operation completes, and then returns the value generated by the asynchronous operation.
+ ///
+ ///
+ /// The invocation result.
+ ///
+ public new TResult EndInvoke()
+ {
+ base.EndInvoke(); // Wait until operation has completed
+ return _result; // Return the result (if above didn't throw)
+ }
+ }
+}
diff --git a/src/Renci.SshNet/Common/BigInteger.cs b/src/Renci.SshNet/Common/BigInteger.cs
index 862af076a..53a659daf 100644
--- a/src/Renci.SshNet/Common/BigInteger.cs
+++ b/src/Renci.SshNet/Common/BigInteger.cs
@@ -1,4 +1,6 @@
-//
+#pragma warning disable SA1028 // Code should not contain trailing whitespace
+#pragma warning disable SA1515 // Single-line comment should be preceded by blank line
+//
// System.Numerics.BigInteger
//
// Authors:
@@ -44,6 +46,8 @@
*
*
* ***************************************************************************/
+#pragma warning restore SA1515 // Single-line comment should be preceded by blank line
+#pragma warning restore SA1028 // Code should not contain trailing whitespace
using System;
using System.Collections.Generic;
@@ -106,7 +110,7 @@ public readonly int BitLength
var msbBitCount = BitScanBackward(_data[msbIndex]) + 1;
- return msbIndex * 4 * 8 + msbBitCount + ((_sign > 0) ? 0 : 1);
+ return (msbIndex * 4 * 8) + msbBitCount + ((_sign > 0) ? 0 : 1);
}
}
@@ -145,8 +149,8 @@ public static BigInteger ModInverse(BigInteger bi, BigInteger modulus)
p1 += (b / a) * p0;
b %= a;
-
}
+
return 0;
}
@@ -210,7 +214,9 @@ public BigInteger(int value)
else
{
_sign = -1;
+#pragma warning disable SA1021 // Negative signs should be spaced correctly
_data = new[] { (uint) -value };
+#pragma warning restore SA1021 // Negative signs should be spaced correctly
}
}
@@ -419,7 +425,9 @@ public BigInteger(byte[] value)
_sign = 1;
}
+#pragma warning disable CA1508 // Avoid dead conditional code | this is the following bug in the analyzer rule: https://github.com/dotnet/roslyn-analyzers/issues/6991
if (_sign == 1)
+#pragma warning restore CA1508 // Avoid dead conditional code
{
while (value[len - 1] == 0)
{
@@ -514,8 +522,9 @@ public BigInteger(byte[] value)
if (borrow != 0)
{
- // FIXME I believe this can't happen, can someone write a test for it?
+#pragma warning disable CA2201 // Do not raise reserved exception types
throw new Exception("non zero final carry");
+#pragma warning restore CA2201 // Do not raise reserved exception types
}
}
}
@@ -535,14 +544,14 @@ private static ulong Mantissa(byte[] v)
var i1 = (uint)v[0] | ((uint)v[1] << 8) | ((uint)v[2] << 16) | ((uint)v[3] << 24);
var i2 = (uint)v[4] | ((uint)v[5] << 8) | ((uint)(v[6] & 0xF) << 16);
- return ((ulong) i1 | ((ulong) i2 << 32));
+ return (ulong) i1 | ((ulong) i2 << 32);
}
///
/// Gets a value indicating whether the value of the current object is an even number.
///
///
- /// if the value of the BigInteger object is an even number; otherwise, .
+ /// if the value of the object is an even number; otherwise, .
///
public readonly bool IsEven
{
@@ -768,7 +777,9 @@ public static BigInteger Zero
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator int(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -813,7 +824,9 @@ public static explicit operator int(BigInteger value)
/// An object that contains the value of the parameter.
///
[CLSCompliant(false)]
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator uint(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -835,7 +848,9 @@ public static explicit operator uint(BigInteger value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator short(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
var val = (int) value;
if (val is < short.MinValue or > short.MaxValue)
@@ -854,7 +869,9 @@ public static explicit operator short(BigInteger value)
/// An object that contains the value of the parameter.
///
[CLSCompliant(false)]
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator ushort(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
var val = (uint) value;
if (val > ushort.MaxValue)
@@ -872,7 +889,9 @@ public static explicit operator ushort(BigInteger value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator byte(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
var val = (uint) value;
if (val > byte.MaxValue)
@@ -891,7 +910,9 @@ public static explicit operator byte(BigInteger value)
/// An object that contains the value of the parameter.
///
[CLSCompliant(false)]
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator sbyte(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
var val = (int) value;
if (val is < sbyte.MinValue or > sbyte.MaxValue)
@@ -909,7 +930,9 @@ public static explicit operator sbyte(BigInteger value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator long(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -973,7 +996,9 @@ long.MinValue works fine since it's bigint encoding looks like a negative
/// An object that contains the value of the parameter.
///
[CLSCompliant(false)]
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator ulong(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -1002,7 +1027,9 @@ public static explicit operator ulong(BigInteger value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator double(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -1041,7 +1068,9 @@ public static explicit operator double(BigInteger value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator float(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return (float) (double) value;
}
@@ -1053,7 +1082,9 @@ public static explicit operator float(BigInteger value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator decimal(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -1092,7 +1123,9 @@ public static explicit operator decimal(BigInteger value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator BigInteger(int value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1105,7 +1138,9 @@ public static implicit operator BigInteger(int value)
/// An object that contains the value of the parameter.
///
[CLSCompliant(false)]
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator BigInteger(uint value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1117,7 +1152,9 @@ public static implicit operator BigInteger(uint value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator BigInteger(short value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1130,7 +1167,9 @@ public static implicit operator BigInteger(short value)
/// An object that contains the value of the parameter.
///
[CLSCompliant(false)]
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator BigInteger(ushort value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1142,20 +1181,24 @@ public static implicit operator BigInteger(ushort value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator BigInteger(byte value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
///
- ///
+ /// Defines an implicit conversion of a signed byte to a value.
///
/// The value to convert to a .
///
/// An object that contains the value of the parameter.
///
[CLSCompliant(false)]
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator BigInteger(sbyte value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1167,7 +1210,9 @@ public static implicit operator BigInteger(sbyte value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator BigInteger(long value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1180,7 +1225,9 @@ public static implicit operator BigInteger(long value)
/// An object that contains the value of the parameter.
///
[CLSCompliant(false)]
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static implicit operator BigInteger(ulong value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1192,7 +1239,9 @@ public static implicit operator BigInteger(ulong value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator BigInteger(double value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1204,7 +1253,9 @@ public static explicit operator BigInteger(double value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator BigInteger(float value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1216,7 +1267,9 @@ public static explicit operator BigInteger(float value)
///
/// An object that contains the value of the parameter.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static explicit operator BigInteger(decimal value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return new BigInteger(value);
}
@@ -1255,7 +1308,7 @@ public static explicit operator BigInteger(decimal value)
if (r > 0)
{
- //left > right
+ // left > right
return new BigInteger(left._sign, CoreSub(left._data, right._data));
}
@@ -1279,7 +1332,9 @@ public static explicit operator BigInteger(decimal value)
if (left._sign == 0)
{
+#pragma warning disable SA1021 // Negative signs should be spaced correctly
return new BigInteger((short) -right._sign, right._data);
+#pragma warning restore SA1021 // Negative signs should be spaced correctly
}
if (left._sign == right._sign)
@@ -1351,7 +1406,7 @@ public static explicit operator BigInteger(decimal value)
ulong carry = 0;
for (var j = 0; j < b.Length; ++j)
{
- carry = carry + ((ulong) ai) * b[j] + res[k];
+ carry = carry + (((ulong) ai) * b[j]) + res[k];
res[k++] = (uint)carry;
carry >>= 32;
}
@@ -1475,7 +1530,9 @@ public static explicit operator BigInteger(decimal value)
return value;
}
+#pragma warning disable SA1021 // Negative signs should be spaced correctly
return new BigInteger((short) -value._sign, value._data);
+#pragma warning restore SA1021 // Negative signs should be spaced correctly
}
///
@@ -1488,7 +1545,9 @@ public static explicit operator BigInteger(decimal value)
///
/// The sign of the operand is unchanged.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator +(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
return value;
}
@@ -1500,7 +1559,9 @@ public static explicit operator BigInteger(decimal value)
///
/// The value of the parameter incremented by 1.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator ++(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -1534,7 +1595,9 @@ public static explicit operator BigInteger(decimal value)
///
/// The value of the parameter decremented by 1.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator --(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -1569,7 +1632,9 @@ public static explicit operator BigInteger(decimal value)
///
/// The result of the bitwise And operation.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator &(BigInteger left, BigInteger right)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (left._sign == 0)
{
@@ -1659,7 +1724,9 @@ public static explicit operator BigInteger(decimal value)
///
/// The result of the bitwise Or operation.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator |(BigInteger left, BigInteger right)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (left._sign == 0)
{
@@ -1749,7 +1816,9 @@ public static explicit operator BigInteger(decimal value)
///
/// The result of the bitwise Or operation.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator ^(BigInteger left, BigInteger right)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (left._sign == 0)
{
@@ -1838,7 +1907,9 @@ public static explicit operator BigInteger(decimal value)
///
/// The bitwise one's complement of .
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator ~(BigInteger value)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (value._data is null)
{
@@ -1895,9 +1966,14 @@ public static explicit operator BigInteger(decimal value)
return new BigInteger(negRes ? (short)-1 : (short)1, result);
}
- //returns the 0-based index of the most significant set bit
- //returns 0 if no bit is set, so extra care when using it
- static int BitScanBackward(uint word)
+ ///
+ /// Returns the zero-based index of the most significant set bit.
+ ///
+ /// The value to scan.
+ ///
+ /// The zero-based index of the most significant set bit, or zero if no bit is set.
+ ///
+ private static int BitScanBackward(uint word)
{
for (var i = 31; i >= 0; --i)
{
@@ -1919,7 +1995,9 @@ static int BitScanBackward(uint word)
///
/// A value that has been shifted to the left by the specified number of bits.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator <<(BigInteger value, int shift)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (shift == 0 || value._data is null)
{
@@ -1976,7 +2054,9 @@ static int BitScanBackward(uint word)
///
/// A value that has been shifted to the right by the specified number of bits.
///
+#pragma warning disable CA2225 // Operator overloads have named alternates
public static BigInteger operator >>(BigInteger value, int shift)
+#pragma warning restore CA2225 // Operator overloads have named alternates
{
if (shift == 0 || value._sign == 0)
{
@@ -2092,7 +2172,6 @@ static int BitScanBackward(uint word)
return left.CompareTo(right) < 0;
}
-
///
/// Returns a value that indicates whether a 64-bit signed integer is less than a value.
///
@@ -2672,7 +2751,11 @@ private readonly string ToStringWithPadding(string format, uint radix, IFormatPr
return additional + baseStr;
}
+#if NET
+ return string.Concat("-", additional, baseStr.AsSpan(1));
+#else
return "-" + additional + baseStr.Substring(1);
+#endif // NET
}
return baseStr;
@@ -2689,9 +2772,9 @@ private static uint[] MakeTwoComplement(uint[] v)
for (var i = 0; i < v.Length; ++i)
{
var word = v[i];
- carry = (ulong)~word + carry;
- word = (uint)carry;
- carry = (uint)(carry >> 32);
+ carry = (ulong) ~word + carry;
+ word = (uint) carry;
+ carry = (uint) (carry >> 32);
res[i] = word;
}
@@ -2713,7 +2796,7 @@ private readonly string ToString(uint radix, IFormatProvider provider)
if (characterSet.Length < radix)
{
- throw new ArgumentException("charSet length less than radix", "characterSet");
+ throw new ArgumentException("charSet length less than radix", nameof(radix));
}
if (radix == 1)
@@ -2731,7 +2814,7 @@ private readonly string ToString(uint radix, IFormatProvider provider)
return _sign == 1 ? "1" : "-1";
}
- var digits = new List(1 + _data.Length * 3 / 10);
+ var digits = new List(1 + ((_data.Length * 3) / 10));
BigInteger a;
if (_sign == 1)
@@ -2910,7 +2993,9 @@ public static bool TryParse(string value, NumberStyles style, IFormatProvider pr
return true;
}
+#pragma warning disable S4136 // Method overloads should be grouped together
private static bool Parse(string value, NumberStyles style, IFormatProvider fp, bool tryParse, out BigInteger result, out Exception exc)
+#pragma warning restore S4136 // Method overloads should be grouped together
{
result = Zero;
exc = null;
@@ -2949,16 +3034,16 @@ private static bool Parse(string value, NumberStyles style, IFormatProvider fp,
return false;
}
- var allowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) != 0;
- var allowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) != 0;
- var allowThousands = (style & NumberStyles.AllowThousands) != 0;
- var allowDecimalPoint = (style & NumberStyles.AllowDecimalPoint) != 0;
- var allowParentheses = (style & NumberStyles.AllowParentheses) != 0;
- var allowTrailingSign = (style & NumberStyles.AllowTrailingSign) != 0;
- var allowLeadingSign = (style & NumberStyles.AllowLeadingSign) != 0;
- var allowTrailingWhite = (style & NumberStyles.AllowTrailingWhite) != 0;
- var allowLeadingWhite = (style & NumberStyles.AllowLeadingWhite) != 0;
- var allowExponent = (style & NumberStyles.AllowExponent) != 0;
+ var allowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) == NumberStyles.AllowCurrencySymbol;
+ var allowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier;
+ var allowThousands = (style & NumberStyles.AllowThousands) == NumberStyles.AllowThousands;
+ var allowDecimalPoint = (style & NumberStyles.AllowDecimalPoint) == NumberStyles.AllowDecimalPoint;
+ var allowParentheses = (style & NumberStyles.AllowParentheses) == NumberStyles.AllowParentheses;
+ var allowTrailingSign = (style & NumberStyles.AllowTrailingSign) == NumberStyles.AllowTrailingSign;
+ var allowLeadingSign = (style & NumberStyles.AllowLeadingSign) == NumberStyles.AllowLeadingSign;
+ var allowTrailingWhite = (style & NumberStyles.AllowTrailingWhite) == NumberStyles.AllowTrailingWhite;
+ var allowLeadingWhite = (style & NumberStyles.AllowLeadingWhite) == NumberStyles.AllowLeadingWhite;
+ var allowExponent = (style & NumberStyles.AllowExponent) == NumberStyles.AllowExponent;
var pos = 0;
@@ -3223,7 +3308,11 @@ private static bool Parse(string value, NumberStyles style, IFormatProvider fp,
{
if (!tryParse)
{
- exc = new OverflowException("Value too large or too small. exp=" + exponent + " rem = " + remainder + " pow = " + Pow(10, -exponent));
+ exc = new OverflowException(string.Format(CultureInfo.InvariantCulture,
+ "Value too large or too small. exp= {0} rem = {1} pow = {2}",
+ exponent,
+ remainder,
+ Pow(10, -exponent)));
}
return false;
@@ -3252,20 +3341,20 @@ private static bool Parse(string value, NumberStyles style, IFormatProvider fp,
private static bool CheckStyle(NumberStyles style, bool tryParse, ref Exception exc)
{
- if ((style & NumberStyles.AllowHexSpecifier) != 0)
+ if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier)
{
var ne = style ^ NumberStyles.AllowHexSpecifier;
- if ((ne & NumberStyles.AllowLeadingWhite) != 0)
+ if ((ne & NumberStyles.AllowLeadingWhite) == NumberStyles.AllowLeadingWhite)
{
ne ^= NumberStyles.AllowLeadingWhite;
}
- if ((ne & NumberStyles.AllowTrailingWhite) != 0)
+ if ((ne & NumberStyles.AllowTrailingWhite) == NumberStyles.AllowTrailingWhite)
{
ne ^= NumberStyles.AllowTrailingWhite;
}
- if (ne != 0)
+ if (ne != NumberStyles.None)
{
if (!tryParse)
{
@@ -3424,7 +3513,7 @@ private static bool ValidDigit(char e, bool allowHex)
return char.IsDigit(e);
}
- private static Exception GetFormatException()
+ private static FormatException GetFormatException()
{
return new FormatException("Input string was not in the correct format");
}
@@ -3545,7 +3634,9 @@ private static bool Parse(string value, bool tryParse, out BigInteger result, ou
{
result = val;
}
+#pragma warning disable CA1508 // Avoid dead conditional code | this is the following bug in the analyzer rule: https://github.com/dotnet/roslyn-analyzers/issues/6991
else if (sign == -1)
+#pragma warning restore CA1508 // Avoid dead conditional code
{
result = new BigInteger(-1, val._data);
}
@@ -3836,7 +3927,6 @@ public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right
g = x;
x = y % x;
y = g;
-
}
if (x.IsZero)
@@ -3926,7 +4016,7 @@ public static double Log(BigInteger value, double baseValue)
{
if ((value._data[length] & (1 << curBit)) != 0)
{
- bitCount = curBit + length * 32;
+ bitCount = curBit + (length * 32);
break;
}
}
@@ -4472,7 +4562,7 @@ public readonly byte[] ToByteArray()
if (carry == 0)
{
var ex = FirstNonFfByte(word);
- var needExtra = (word & (1 << (ex * 8 - 1))) == 0;
+ var needExtra = (word & (1 << ((ex * 8) - 1))) == 0;
var to = ex + (needExtra ? 1 : 0);
if (to != extra)
@@ -4488,7 +4578,9 @@ public readonly byte[] ToByteArray()
if (needExtra)
{
+#pragma warning disable S1854 // Unused assignments should be removed
res[j++] = 0xFF;
+#pragma warning restore S1854 // Unused assignments should be removed
}
}
else
@@ -4498,7 +4590,9 @@ public readonly byte[] ToByteArray()
res[j++] = (byte)(word >> 8);
res[j++] = (byte)(word >> 16);
res[j++] = (byte)(word >> 24);
+#pragma warning disable S1854 // Unused assignments should be removed
res[j++] = 0xFF;
+#pragma warning restore S1854 // Unused assignments should be removed
}
}
@@ -4807,14 +4901,14 @@ private static void DivModUnsigned(uint[] u, uint[] v, out uint[] q, out uint[]
{
int i;
- var rr = Base * un[j + n] + un[j + n - 1];
+ var rr = (Base * un[j + n]) + un[j + n - 1];
var qq = rr / vn[n - 1];
rr -= qq * vn[n - 1];
- for (;;)
+ for (; ; )
{
// Estimate too big ?
- if ((qq >= Base) || (qq * vn[n - 2] > (rr * Base + un[j + n - 2])))
+ if ((qq >= Base) || (qq * vn[n - 2] > ((rr * Base) + un[j + n - 2])))
{
qq--;
rr += (ulong)vn[n - 1];
@@ -4827,7 +4921,6 @@ private static void DivModUnsigned(uint[] u, uint[] v, out uint[] q, out uint[]
break;
}
-
// Multiply and subtract
long b = 0;
long t;
diff --git a/src/Renci.SshNet/Common/ChannelDataEventArgs.cs b/src/Renci.SshNet/Common/ChannelDataEventArgs.cs
index 4891fe240..dbc20b5be 100644
--- a/src/Renci.SshNet/Common/ChannelDataEventArgs.cs
+++ b/src/Renci.SshNet/Common/ChannelDataEventArgs.cs
@@ -1,4 +1,6 @@
-namespace Renci.SshNet.Common
+using System;
+
+namespace Renci.SshNet.Common
{
///
/// Provides data for event.
@@ -10,9 +12,15 @@ internal class ChannelDataEventArgs : ChannelEventArgs
///
/// Channel number.
/// Channel data.
+ /// is .
public ChannelDataEventArgs(uint channelNumber, byte[] data)
: base(channelNumber)
{
+ if (data is null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
Data = data;
}
diff --git a/src/Renci.SshNet/Common/ChannelRequestEventArgs.cs b/src/Renci.SshNet/Common/ChannelRequestEventArgs.cs
index 7747b7621..95548194d 100644
--- a/src/Renci.SshNet/Common/ChannelRequestEventArgs.cs
+++ b/src/Renci.SshNet/Common/ChannelRequestEventArgs.cs
@@ -13,8 +13,14 @@ internal sealed class ChannelRequestEventArgs : EventArgs
/// Initializes a new instance of the class.
///
/// Request information.
+ /// is .
public ChannelRequestEventArgs(RequestInfo info)
{
+ if (info is null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
Info = info;
}
diff --git a/src/Renci.SshNet/Common/DerData.cs b/src/Renci.SshNet/Common/DerData.cs
index 29361ace5..ba0a678dd 100644
--- a/src/Renci.SshNet/Common/DerData.cs
+++ b/src/Renci.SshNet/Common/DerData.cs
@@ -48,7 +48,7 @@ public DerData()
/// Initializes a new instance of the class.
///
/// DER encoded data.
- /// its a construct
+ /// its a construct.
public DerData(byte[] data, bool construct = false)
{
_data = new List(data);
diff --git a/src/Renci.SshNet/Common/Extensions.cs b/src/Renci.SshNet/Common/Extensions.cs
index 03f05be87..f364dc9f4 100644
--- a/src/Renci.SshNet/Common/Extensions.cs
+++ b/src/Renci.SshNet/Common/Extensions.cs
@@ -50,7 +50,7 @@ internal static BigInteger ToBigInteger(this byte[] data)
}
///
- /// Initializes a new instance of the structure using the SSH BigNum2 Format
+ /// Initializes a new instance of the structure using the SSH BigNum2 Format.
///
public static BigInteger ToBigInteger2(this byte[] data)
{
@@ -60,6 +60,7 @@ public static BigInteger ToBigInteger2(this byte[] data)
Buffer.BlockCopy(data, 0, buf, 1, data.Length);
data = buf;
}
+
return data.ToBigInteger();
}
diff --git a/src/Renci.SshNet/Common/HostKeyEventArgs.cs b/src/Renci.SshNet/Common/HostKeyEventArgs.cs
index 79e6aac8c..acd38965a 100644
--- a/src/Renci.SshNet/Common/HostKeyEventArgs.cs
+++ b/src/Renci.SshNet/Common/HostKeyEventArgs.cs
@@ -30,7 +30,7 @@ public class HostKeyEventArgs : EventArgs
///
/// Gets the host key name.
///
- public string HostKeyName{ get; private set; }
+ public string HostKeyName { get; private set; }
///
/// Gets the MD5 fingerprint.
@@ -50,7 +50,7 @@ public byte[] FingerPrint
/// Gets the SHA256 fingerprint of the host key in the same format as the ssh command,
/// i.e. non-padded base64, but without the SHA256: prefix.
///
- /// ohD8VZEXGWo6Ez8GSEJQ9WpafgLFsOfLOtGGQCQo6Og
+ /// ohD8VZEXGWo6Ez8GSEJQ9WpafgLFsOfLOtGGQCQo6Og.
///
/// Base64 encoded SHA256 fingerprint with padding (equals sign) removed.
///
@@ -66,7 +66,7 @@ public string FingerPrintSHA256
/// Gets the MD5 fingerprint of the host key in the same format as the ssh command,
/// i.e. hexadecimal bytes separated by colons, but without the MD5: prefix.
///
- /// 97:70:33:82:fd:29:3a:73:39:af:6a:07:ad:f8:80:49
+ /// 97:70:33:82:fd:29:3a:73:39:af:6a:07:ad:f8:80:49.
public string FingerPrintMD5
{
get
@@ -87,27 +87,43 @@ public string FingerPrintMD5
/// Initializes a new instance of the class.
///
/// The host.
+ /// is .
public HostKeyEventArgs(KeyHostAlgorithm host)
{
+ if (host is null)
+ {
+ throw new ArgumentNullException(nameof(host));
+ }
+
CanTrust = true;
HostKey = host.Data;
HostKeyName = host.Name;
KeyLength = host.Key.KeyLength;
-
+
_lazyFingerPrint = new Lazy(() =>
- {
- using var md5 = CryptoAbstraction.CreateMD5();
- return md5.ComputeHash(HostKey);
- });
+ {
+ using var md5 = CryptoAbstraction.CreateMD5();
+ return md5.ComputeHash(HostKey);
+ });
_lazyFingerPrintSHA256 = new Lazy(() =>
- {
- using var sha256 = CryptoAbstraction.CreateSHA256();
- return Convert.ToBase64String(sha256.ComputeHash(HostKey)).Replace("=", "");
- });
+ {
+ using var sha256 = CryptoAbstraction.CreateSHA256();
+
+ return Convert.ToBase64String(sha256.ComputeHash(HostKey))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ .Replace("=", string.Empty, StringComparison.Ordinal);
+#else
+ .Replace("=", string.Empty);
+#endif // NET || NETSTANDARD2_1_OR_GREATER
+ });
- _lazyFingerPrintMD5 = new Lazy(() =>
- BitConverter.ToString(FingerPrint).Replace("-", ":").ToLowerInvariant());
+ _lazyFingerPrintMD5 = new Lazy(() =>
+ {
+#pragma warning disable CA1308 // Normalize strings to uppercase
+ return BitConverter.ToString(FingerPrint).Replace('-', ':').ToLowerInvariant();
+#pragma warning restore CA1308 // Normalize strings to uppercase
+ });
}
}
}
diff --git a/src/Renci.SshNet/Common/NetConfServerException.cs b/src/Renci.SshNet/Common/NetConfServerException.cs
index 0d807f0c7..e8614bcb3 100644
--- a/src/Renci.SshNet/Common/NetConfServerException.cs
+++ b/src/Renci.SshNet/Common/NetConfServerException.cs
@@ -41,7 +41,7 @@ public NetConfServerException(string message, Exception innerException)
#if FEATURE_BINARY_SERIALIZATION
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The that holds the serialized object data about the exception being thrown.
/// The that contains contextual information about the source or destination.
diff --git a/src/Renci.SshNet/Common/ObjectIdentifier.cs b/src/Renci.SshNet/Common/ObjectIdentifier.cs
index a02a40908..5aa310a54 100644
--- a/src/Renci.SshNet/Common/ObjectIdentifier.cs
+++ b/src/Renci.SshNet/Common/ObjectIdentifier.cs
@@ -7,7 +7,9 @@ namespace Renci.SshNet.Common
///
/// Describes object identifier for DER encoding.
///
+#pragma warning disable CA1815 // Override equals and operator equals on value types
public struct ObjectIdentifier
+#pragma warning restore CA1815 // Override equals and operator equals on value types
{
///
/// Gets the object identifier.
diff --git a/src/Renci.SshNet/Common/PipeStream.cs b/src/Renci.SshNet/Common/PipeStream.cs
index 887766f3b..da7b89cba 100644
--- a/src/Renci.SshNet/Common/PipeStream.cs
+++ b/src/Renci.SshNet/Common/PipeStream.cs
@@ -10,8 +10,6 @@ namespace Renci.SshNet.Common
/// PipeStream is a thread-safe read/write data stream for use between two threads in a
/// single-producer/single-consumer type problem.
///
- /// 2006/10/13 1.0
- /// Update on 2008/10/9 1.1 - uses Monitor instead of Manual Reset events for more elegant synchronicity.
///
/// Copyright (c) 2006 James Kolpack (james dot kolpack at google mail)
///
@@ -46,11 +44,6 @@ public class PipeStream : Stream
///
private bool _isFlushed;
- ///
- /// Maximum number of bytes to store in the buffer.
- ///
- private long _maxBufferLength = 200 * 1024 * 1024;
-
///
/// Setting this to true will cause Read() to block if it appears
/// that it will run out of data.
@@ -66,11 +59,7 @@ public class PipeStream : Stream
/// Gets or sets the maximum number of bytes to store in the buffer.
///
/// The length of the max buffer.
- public long MaxBufferLength
- {
- get { return _maxBufferLength; }
- set { _maxBufferLength = value; }
- }
+ public long MaxBufferLength { get; set; } = 200 * 1024 * 1024;
///
/// Gets or sets a value indicating whether to block last read method before the buffer is empty.
@@ -88,19 +77,27 @@ public bool BlockLastReadBuffer
{
get
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
if (_isDisposed)
{
throw CreateObjectDisposedException();
}
+#endif // NET7_0_OR_GREATER
return _canBlockLastRead;
}
set
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
if (_isDisposed)
{
throw CreateObjectDisposedException();
}
+#endif // NET7_0_OR_GREATER
_canBlockLastRead = value;
@@ -126,10 +123,14 @@ public bool BlockLastReadBuffer
///
public override void Flush()
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
if (_isDisposed)
{
throw CreateObjectDisposedException();
}
+#endif // NET7_0_OR_GREATER
_isFlushed = true;
lock (_buffer)
@@ -200,15 +201,19 @@ public override int Read(byte[] buffer, int offset, int count)
throw new ArgumentOutOfRangeException(nameof(offset), "offset or count is negative.");
}
- if (BlockLastReadBuffer && count >= _maxBufferLength)
+ if (BlockLastReadBuffer && count >= MaxBufferLength)
{
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "count({0}) > mMaxBufferLength({1})", count, _maxBufferLength));
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "count({0}) > mMaxBufferLength({1})", count, MaxBufferLength));
}
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
if (_isDisposed)
{
throw CreateObjectDisposedException();
}
+#endif // NET7_0_OR_GREATER
if (count == 0)
{
@@ -284,10 +289,14 @@ public override void Write(byte[] buffer, int offset, int count)
throw new ArgumentOutOfRangeException(nameof(offset), "offset or count is negative.");
}
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
if (_isDisposed)
{
throw CreateObjectDisposedException();
}
+#endif // NET7_0_OR_GREATER
if (count == 0)
{
@@ -297,7 +306,7 @@ public override void Write(byte[] buffer, int offset, int count)
lock (_buffer)
{
// wait until the buffer isn't full
- while (Length >= _maxBufferLength)
+ while (Length >= MaxBufferLength)
{
_ = Monitor.Wait(_buffer);
}
@@ -380,10 +389,14 @@ public override long Length
{
get
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
if (_isDisposed)
{
throw CreateObjectDisposedException();
}
+#endif // NET7_0_OR_GREATER
return _buffer.Count;
}
@@ -402,9 +415,11 @@ public override long Position
set { throw new NotSupportedException(); }
}
+#if !NET7_0_OR_GREATER
private ObjectDisposedException CreateObjectDisposedException()
{
return new ObjectDisposedException(GetType().FullName);
}
+#endif // !NET7_0_OR_GREATER
}
}
diff --git a/src/Renci.SshNet/Common/SemaphoreLight.cs b/src/Renci.SshNet/Common/SemaphoreLight.cs
index d506d44e0..106b10180 100644
--- a/src/Renci.SshNet/Common/SemaphoreLight.cs
+++ b/src/Renci.SshNet/Common/SemaphoreLight.cs
@@ -6,7 +6,7 @@ namespace Renci.SshNet.Common
///
/// Light implementation of SemaphoreSlim.
///
- public class SemaphoreLight : IDisposable
+ public sealed class SemaphoreLight : IDisposable
{
private readonly object _lock = new object();
private ManualResetEvent _waitHandle;
@@ -228,7 +228,7 @@ public void Dispose()
/// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
- protected void Dispose(bool disposing)
+ private void Dispose(bool disposing)
{
if (disposing)
{
diff --git a/src/Renci.SshNet/Common/SshConnectionException.cs b/src/Renci.SshNet/Common/SshConnectionException.cs
index 75c7227a1..a5c227b8e 100644
--- a/src/Renci.SshNet/Common/SshConnectionException.cs
+++ b/src/Renci.SshNet/Common/SshConnectionException.cs
@@ -47,6 +47,17 @@ public SshConnectionException(string message, DisconnectReason disconnectReasonC
DisconnectReason = disconnectReasonCode;
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message.
+ /// The inner.
+ public SshConnectionException(string message, Exception inner)
+ : base(message, inner)
+ {
+ DisconnectReason = DisconnectReason.None;
+ }
+
///
/// Initializes a new instance of the class.
///
diff --git a/src/Renci.SshNet/Common/SshData.cs b/src/Renci.SshNet/Common/SshData.cs
index 5dd9fa9ea..9cbf4fd95 100644
--- a/src/Renci.SshNet/Common/SshData.cs
+++ b/src/Renci.SshNet/Common/SshData.cs
@@ -7,7 +7,9 @@ namespace Renci.SshNet.Common
///
/// Base ssh data serialization type.
///
+#pragma warning disable CA1001 // Types that own disposable fields should be disposable
public abstract class SshData
+#pragma warning restore CA1001 // Types that own disposable fields should be disposable
{
internal const int DefaultCapacity = 64;
@@ -63,9 +65,12 @@ public byte[] GetBytes()
{
var messageLength = BufferCapacity;
var capacity = messageLength != -1 ? messageLength : DefaultCapacity;
- var dataStream = new SshDataStream(capacity);
- WriteBytes(dataStream);
- return dataStream.ToArray();
+
+ using (var dataStream = new SshDataStream(capacity))
+ {
+ WriteBytes(dataStream);
+ return dataStream.ToArray();
+ }
}
///
@@ -129,7 +134,9 @@ private void LoadInternal(byte[] value, int offset, int count)
///
/// Reads all data left in internal buffer at current position.
///
- /// An array of bytes containing the remaining data in the internal buffer.
+ ///
+ /// An array of bytes containing the remaining data in the internal buffer.
+ ///
protected byte[] ReadBytes()
{
var bytesLength = (int) (_stream.Length - _stream.Position);
@@ -142,16 +149,12 @@ protected byte[] ReadBytes()
/// Reads next specified number of bytes data type from internal buffer.
///
/// Number of bytes to read.
- /// An array of bytes that was read from the internal buffer.
- /// is greater than the internal buffer size.
+ ///
+ /// An array of bytes that was read from the internal buffer.
+ ///
+ /// is greater than the number of bytes available to be read.
protected byte[] ReadBytes(int length)
{
- /*
- * Note that this also prevents allocating non-relevant lengths, such as if length is greater than _data.Count but less than int.MaxValue.
- * For the nerds, the condition translates to: if (length > data.Count && length < int.MaxValue)
- * Which probably would cause all sorts of exception, most notably OutOfMemoryException.
- */
-
var data = new byte[length];
var bytesRead = _stream.Read(data, 0, length);
@@ -166,7 +169,10 @@ protected byte[] ReadBytes(int length)
///
/// Reads next byte data type from internal buffer.
///
- /// Byte read.
+ ///
+ /// The read.
+ ///
+ /// Attempt to read past the end of the stream.
protected byte ReadByte()
{
var byteRead = _stream.ReadByte();
@@ -184,6 +190,7 @@ protected byte ReadByte()
///
/// The that was read.
///
+ /// Attempt to read past the end of the stream.
protected bool ReadBoolean()
{
return ReadByte() != 0;
@@ -195,6 +202,7 @@ protected bool ReadBoolean()
///
/// The that was read.
///
+ /// Attempt to read past the end of the stream.
protected ushort ReadUInt16()
{
return Pack.BigEndianToUInt16(ReadBytes(2));
@@ -206,6 +214,7 @@ protected ushort ReadUInt16()
///
/// The that was read.
///
+ /// Attempt to read past the end of the stream.
protected uint ReadUInt32()
{
return Pack.BigEndianToUInt32(ReadBytes(4));
@@ -217,6 +226,7 @@ protected uint ReadUInt32()
///
/// The that was read.
///
+ /// Attempt to read past the end of the stream.
protected ulong ReadUInt64()
{
return Pack.BigEndianToUInt64(ReadBytes(8));
@@ -260,8 +270,10 @@ protected string[] ReadNamesList()
///
/// Reads next extension-pair data type from internal buffer.
///
- /// Extensions pair dictionary.
- protected IDictionary ReadExtensionPair()
+ ///
+ /// Extensions pair dictionary.
+ ///
+ protected Dictionary ReadExtensionPair()
{
var result = new Dictionary();
@@ -373,7 +385,11 @@ protected void Write(BigInteger data)
/// name-list data to write.
protected void Write(string[] data)
{
+#if NET || NETSTANDARD2_1_OR_GREATER
+ Write(string.Join(',', data), Ascii);
+#else
Write(string.Join(",", data), Ascii);
+#endif // NET || NETSTANDARD2_1_OR_GREATER
}
///
diff --git a/src/Renci.SshNet/Common/SshDataStream.cs b/src/Renci.SshNet/Common/SshDataStream.cs
index 3a15d7438..6bad5f21f 100644
--- a/src/Renci.SshNet/Common/SshDataStream.cs
+++ b/src/Renci.SshNet/Common/SshDataStream.cs
@@ -101,6 +101,24 @@ public void Write(byte[] data)
Write(data, 0, data.Length);
}
+ ///
+ /// Writes string data to the SSH data stream using the specified encoding.
+ ///
+ /// The string data to write.
+ /// The character encoding to use.
+ /// is .
+ /// is .
+ public void Write(string s, Encoding encoding)
+ {
+ if (encoding is null)
+ {
+ throw new ArgumentNullException(nameof(encoding));
+ }
+
+ var bytes = encoding.GetBytes(s);
+ WriteBinary(bytes, 0, bytes.Length);
+ }
+
///
/// Reads a byte array from the SSH data stream.
///
@@ -149,24 +167,6 @@ public void WriteBinary(byte[] buffer, int offset, int count)
Write(buffer, offset, count);
}
- ///
- /// Writes string data to the SSH data stream using the specified encoding.
- ///
- /// The string data to write.
- /// The character encoding to use.
- /// is .
- /// is .
- public void Write(string s, Encoding encoding)
- {
- if (encoding is null)
- {
- throw new ArgumentNullException(nameof(encoding));
- }
-
- var bytes = encoding.GetBytes(s);
- WriteBinary(bytes, 0, bytes.Length);
- }
-
///
/// Reads a from the SSH datastream.
///
diff --git a/src/Renci.SshNet/Common/TerminalModes.cs b/src/Renci.SshNet/Common/TerminalModes.cs
index 8737872b8..6b813837a 100644
--- a/src/Renci.SshNet/Common/TerminalModes.cs
+++ b/src/Renci.SshNet/Common/TerminalModes.cs
@@ -1,10 +1,13 @@
namespace Renci.SshNet.Common
{
///
- /// Specifies the initial assignments of the opcode values that are used in the 'encoded terminal modes' valu
+ /// Specifies the initial assignments of the opcode values that are used in the 'encoded terminal modes' value.
///
+#pragma warning disable CA1028 // Enum Storage should be Int32
public enum TerminalModes : byte
+#pragma warning restore CA1028 // Enum Storage should be Int32
{
+#pragma warning disable CA1707 // Identifiers should not contain underscores
///
/// Indicates end of options.
///
@@ -289,5 +292,6 @@ public enum TerminalModes : byte
/// Specifies the output baud rate in bits per second.
///
TTY_OP_OSPEED = 129,
+#pragma warning restore CA1707 // Identifiers should not contain underscores
}
}
diff --git a/src/Renci.SshNet/Compression/CompressionMode.cs b/src/Renci.SshNet/Compression/CompressionMode.cs
index 1577e0bc8..b0b6f6b94 100644
--- a/src/Renci.SshNet/Compression/CompressionMode.cs
+++ b/src/Renci.SshNet/Compression/CompressionMode.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Compression
{
///
- /// Specifies compression modes
+ /// Specifies compression modes.
///
public enum CompressionMode
{
diff --git a/src/Renci.SshNet/Compression/Compressor.cs b/src/Renci.SshNet/Compression/Compressor.cs
index b63a292ed..63eb3e336 100644
--- a/src/Renci.SshNet/Compression/Compressor.cs
+++ b/src/Renci.SshNet/Compression/Compressor.cs
@@ -6,7 +6,7 @@
namespace Renci.SshNet.Compression
{
///
- /// Represents base class for compression algorithm implementation
+ /// Represents base class for compression algorithm implementation.
///
public abstract class Compressor : Algorithm, IDisposable
{
@@ -20,7 +20,7 @@ public abstract class Compressor : Algorithm, IDisposable
/// Gets or sets a value indicating whether compression is active.
///
///
- /// if compression is active; otherwise, .
+ /// if compression is active; otherwise, .
///
protected bool IsActive { get; set; }
@@ -42,7 +42,7 @@ protected Compressor()
}
///
- /// Initializes the algorithm
+ /// Initializes the algorithm.
///
/// The session.
public virtual void Init(Session session)
@@ -54,7 +54,9 @@ public virtual void Init(Session session)
/// Compresses the specified data.
///
/// Data to compress.
- /// Compressed data
+ ///
+ /// The compressed data.
+ ///
public virtual byte[] Compress(byte[] data)
{
return Compress(data, 0, data.Length);
@@ -142,7 +144,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/Compression/Zlib.cs b/src/Renci.SshNet/Compression/Zlib.cs
index 0dc5918c4..504697fd7 100644
--- a/src/Renci.SshNet/Compression/Zlib.cs
+++ b/src/Renci.SshNet/Compression/Zlib.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Compression
{
///
- /// Represents "zlib" compression implementation
+ /// Represents "zlib" compression implementation.
///
internal sealed class Zlib : Compressor
{
@@ -14,7 +14,7 @@ public override string Name
}
///
- /// Initializes the algorithm
+ /// Initializes the algorithm.
///
/// The session.
public override void Init(Session session)
diff --git a/src/Renci.SshNet/Compression/ZlibOpenSsh.cs b/src/Renci.SshNet/Compression/ZlibOpenSsh.cs
index 177d76b49..45bf1165f 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.org" compression implementation.
///
public class ZlibOpenSsh : Compressor
{
@@ -16,7 +16,7 @@ public override string Name
}
///
- /// Initializes the algorithm
+ /// Initializes the algorithm.
///
/// The session.
public override void Init(Session session)
@@ -32,4 +32,4 @@ private void Session_UserAuthenticationSuccessReceived(object sender, MessageEve
Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet/Compression/ZlibStream.cs b/src/Renci.SshNet/Compression/ZlibStream.cs
index 5c9d3b841..e09244992 100644
--- a/src/Renci.SshNet/Compression/ZlibStream.cs
+++ b/src/Renci.SshNet/Compression/ZlibStream.cs
@@ -1,11 +1,16 @@
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
public class ZlibStream
+#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
{
//private readonly Ionic.Zlib.ZlibStream _baseStream;
@@ -45,5 +50,9 @@ public void Write(byte[] buffer, int offset, int count)
{
//this._baseStream.Write(buffer, offset, count);
}
+#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/Connection/IConnector.cs b/src/Renci.SshNet/Connection/IConnector.cs
index e49587b74..6ce454eb2 100644
--- a/src/Renci.SshNet/Connection/IConnector.cs
+++ b/src/Renci.SshNet/Connection/IConnector.cs
@@ -3,10 +3,28 @@
namespace Renci.SshNet.Connection
{
+ ///
+ /// Represents a means to connect to a SSH endpoint.
+ ///
internal interface IConnector
{
+ ///
+ /// Connects to a SSH endpoint using the specified .
+ ///
+ /// The to use to establish a connection to a SSH endpoint.
+ ///
+ /// A connected to the SSH endpoint represented by the specified .
+ ///
Socket Connect(IConnectionInfo connectionInfo);
+ ///
+ /// Asynchronously connects to a SSH endpoint using the specified .
+ ///
+ /// The to use to establish a connection to a SSH endpoint.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A connected to the SSH endpoint represented by the specified .
+ ///
System.Threading.Tasks.Task ConnectAsync(IConnectionInfo connectionInfo, CancellationToken cancellationToken);
}
}
diff --git a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs
index 252cda986..1cbe3d7f2 100644
--- a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs
+++ b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs
@@ -1,5 +1,7 @@
using System;
using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
namespace Renci.SshNet.Connection
{
@@ -19,6 +21,16 @@ internal interface IProtocolVersionExchange
///
SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout);
- System.Threading.Tasks.Task StartAsync(string clientVersion, Socket socket, System.Threading.CancellationToken cancellationToken);
+ ///
+ /// Asynchronously performs the SSH protocol version exchange.
+ ///
+ /// The identification string of the SSH client.
+ /// A connected to the server.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the SSH protocol version exchange. The value of its
+ /// contains the SSH identification of the server.
+ ///
+ Task StartAsync(string clientVersion, Socket socket, CancellationToken cancellationToken);
}
}
diff --git a/src/Renci.SshNet/Connection/ISocketFactory.cs b/src/Renci.SshNet/Connection/ISocketFactory.cs
index 0d76eeb29..a12233182 100644
--- a/src/Renci.SshNet/Connection/ISocketFactory.cs
+++ b/src/Renci.SshNet/Connection/ISocketFactory.cs
@@ -2,8 +2,22 @@
namespace Renci.SshNet.Connection
{
+ ///
+ /// Represents a factory to create instances.
+ ///
internal interface ISocketFactory
{
+ ///
+ /// Creates a with the specified ,
+ /// and that does not use the
+ /// Nagle algorithm.
+ ///
+ /// The .
+ /// The .
+ /// The .
+ ///
+ /// The .
+ ///
Socket Create(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType);
}
}
diff --git a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs
index fb0203f17..00c5d0573 100644
--- a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs
+++ b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs
@@ -23,7 +23,7 @@ internal sealed class ProtocolVersionExchange : IProtocolVersionExchange
{
private const byte Null = 0x00;
- private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+?)([ ](?.+))?$", RegexOptions.Compiled);
+ private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+?)([ ](?.+))?$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
///
/// Performs the SSH protocol version exchange.
@@ -67,6 +67,16 @@ public SshIdentification Start(string clientVersion, Socket socket, TimeSpan tim
}
}
+ ///
+ /// Asynchronously performs the SSH protocol version exchange.
+ ///
+ /// The identification string of the SSH client.
+ /// A connected to the server.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the SSH protocol version exchange. The value of its
+ /// contains the SSH identification of the server.
+ ///
public async Task StartAsync(string clientVersion, Socket socket, CancellationToken cancellationToken)
{
// Immediately send the identification string since the spec states both sides MUST send an identification string
diff --git a/src/Renci.SshNet/Connection/ProxyConnector.cs b/src/Renci.SshNet/Connection/ProxyConnector.cs
index 089ee562a..d8261e024 100644
--- a/src/Renci.SshNet/Connection/ProxyConnector.cs
+++ b/src/Renci.SshNet/Connection/ProxyConnector.cs
@@ -5,6 +5,10 @@
namespace Renci.SshNet.Connection
{
+ ///
+ /// Represents a connector that uses a proxy server to establish a connection to a given SSH
+ /// endpoint.
+ ///
internal abstract class ProxyConnector : ConnectorBase
{
protected ProxyConnector(ISocketFactory socketFactory)
@@ -15,18 +19,37 @@ protected ProxyConnector(ISocketFactory socketFactory)
protected abstract void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket);
// ToDo: Performs async/sync fallback, true async version should be implemented in derived classes
- protected virtual Task HandleProxyConnectAsync(IConnectionInfo connectionInfo, Socket socket, CancellationToken cancellationToken)
+ protected virtual
+#if NET || NETSTANDARD2_1_OR_GREATER
+ async
+#endif // NET || NETSTANDARD2_1_OR_GREATER
+ Task HandleProxyConnectAsync(IConnectionInfo connectionInfo, Socket socket, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
- using (cancellationToken.Register(o => ((Socket)o).Dispose(), socket, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(o => ((Socket)o).Dispose(), socket, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(o => ((Socket) o).Dispose(), socket, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
+#pragma warning disable MA0042 // Do not use blocking calls in an async method; false positive caused by https://github.com/meziantou/Meziantou.Analyzer/issues/613
HandleProxyConnect(connectionInfo, socket);
+#pragma warning restore MA0042 // Do not use blocking calls in an async method
}
+#if !NET && !NETSTANDARD2_1_OR_GREATER
return Task.CompletedTask;
+#endif // !NET && !NETSTANDARD2_1_OR_GREATER
}
+ ///
+ /// Connects to a SSH endpoint using the specified .
+ ///
+ /// The to use to establish a connection to a SSH endpoint.
+ ///
+ /// A connected to the SSH endpoint represented by the specified .
+ ///
public override Socket Connect(IConnectionInfo connectionInfo)
{
var socket = SocketConnect(connectionInfo.ProxyHost, connectionInfo.ProxyPort, connectionInfo.Timeout);
@@ -45,6 +68,14 @@ public override Socket Connect(IConnectionInfo connectionInfo)
}
}
+ ///
+ /// Asynchronously connects to a SSH endpoint using the specified .
+ ///
+ /// The to use to establish a connection to a SSH endpoint.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A connected to the SSH endpoint represented by the specified .
+ ///
public override async Task ConnectAsync(IConnectionInfo connectionInfo, CancellationToken cancellationToken)
{
var socket = await SocketConnectAsync(connectionInfo.ProxyHost, connectionInfo.ProxyPort, cancellationToken).ConfigureAwait(false);
diff --git a/src/Renci.SshNet/Connection/SocketFactory.cs b/src/Renci.SshNet/Connection/SocketFactory.cs
index d279da288..698634947 100644
--- a/src/Renci.SshNet/Connection/SocketFactory.cs
+++ b/src/Renci.SshNet/Connection/SocketFactory.cs
@@ -2,11 +2,25 @@
namespace Renci.SshNet.Connection
{
+ ///
+ /// Represents a factory to create instances.
+ ///
internal sealed class SocketFactory : ISocketFactory
{
+ ///
+ /// Creates a with the specified ,
+ /// and that does not use the
+ /// Nagle algorithm.
+ ///
+ /// The .
+ /// The .
+ /// The .
+ ///
+ /// The .
+ ///
public Socket Create(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
- return new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
+ return new Socket(addressFamily, socketType, protocolType) { NoDelay = true };
}
}
}
diff --git a/src/Renci.SshNet/Connection/Socks4Connector.cs b/src/Renci.SshNet/Connection/Socks4Connector.cs
index 4da6bf773..d679c17a3 100644
--- a/src/Renci.SshNet/Connection/Socks4Connector.cs
+++ b/src/Renci.SshNet/Connection/Socks4Connector.cs
@@ -62,21 +62,23 @@ private static byte[] CreateSocks4ConnectionRequest(string hostname, ushort port
var addressBytes = GetSocks4DestinationAddress(hostname);
var proxyUserBytes = GetProxyUserBytes(username);
- var connectionRequest = new byte
- [
- // SOCKS version number
- 1 +
- // Command code
- 1 +
- // Port number
- 2 +
- // IP address
- addressBytes.Length +
- // Username
- proxyUserBytes.Length +
- // Null terminator
- 1
- ];
+ var connectionRequest = new byte[// SOCKS version number
+ 1 +
+
+ // Command code
+ 1 +
+
+ // Port number
+ 2 +
+
+ // IP address
+ addressBytes.Length +
+
+ // Username
+ proxyUserBytes.Length +
+
+ // Null terminator
+ 1];
var index = 0;
diff --git a/src/Renci.SshNet/Connection/Socks5Connector.cs b/src/Renci.SshNet/Connection/Socks5Connector.cs
index ef9f9974c..a1ed5f751 100644
--- a/src/Renci.SshNet/Connection/Socks5Connector.cs
+++ b/src/Renci.SshNet/Connection/Socks5Connector.cs
@@ -30,10 +30,13 @@ protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socke
{
// SOCKS version number
0x05,
+
// Number of supported authentication methods
0x02,
+
// No authentication
0x00,
+
// Username/Password authentication
0x02
};
@@ -156,19 +159,20 @@ private static byte[] CreateSocks5UserNameAndPasswordAuthenticationRequest(strin
throw new ProxyException("Proxy password is too long.");
}
- var authenticationRequest = new byte
- [
- // Version of the negotiation
- 1 +
- // Length of the username
- 1 +
- // Username
- username.Length +
- // Length of the password
- 1 +
- // Password
- password.Length
- ];
+ var authenticationRequest = new byte[// Version of the negotiation
+ 1 +
+
+ // Length of the username
+ 1 +
+
+ // Username
+ username.Length +
+
+ // Length of the password
+ 1 +
+
+ // Password
+ password.Length];
var index = 0;
@@ -195,21 +199,23 @@ private static byte[] CreateSocks5ConnectionRequest(string hostname, ushort port
{
var addressBytes = GetSocks5DestinationAddress(hostname, out var addressType);
- var connectionRequest = new byte
- [
- // SOCKS version number
- 1 +
- // Command code
- 1 +
- // Reserved
- 1 +
- // Address type
- 1 +
- // Address
- addressBytes.Length +
- // Port number
- 2
- ];
+ var connectionRequest = new byte[// SOCKS version number
+ 1 +
+
+ // Command code
+ 1 +
+
+ // Reserved
+ 1 +
+
+ // Address type
+ 1 +
+
+ // Address
+ addressBytes.Length +
+
+ // Port number
+ 2];
var index = 0;
diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs
index 1f109c279..02e817b24 100644
--- a/src/Renci.SshNet/ConnectionInfo.cs
+++ b/src/Renci.SshNet/ConnectionInfo.cs
@@ -398,8 +398,10 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
{ "ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data) },
{ "ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data) },
{ "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data) },
- { "rsa-sha2-512", data => { var key = new RsaKey(); return new KeyHostAlgorithm("rsa-sha2-512", key, data, new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); }},
- { "rsa-sha2-256", data => { var key = new RsaKey(); return new KeyHostAlgorithm("rsa-sha2-256", key, data, new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); }},
+#pragma warning disable SA1107 // Code should not contain multiple statements on one line
+ { "rsa-sha2-512", data => { var key = new RsaKey(); return new KeyHostAlgorithm("rsa-sha2-512", key, data, new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); } },
+ { "rsa-sha2-256", data => { var key = new RsaKey(); return new KeyHostAlgorithm("rsa-sha2-256", key, data, new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); } },
+#pragma warning restore SA1107 // Code should not contain multiple statements on one line
{ "ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data) },
{ "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data) },
};
@@ -470,14 +472,28 @@ void IConnectionInfoInternal.UserAuthenticationBannerReceived(object sender, Mes
AuthenticationBanner?.Invoke(this, new AuthenticationBannerEventArgs(Username, e.Message.Message, e.Message.Language));
}
+ ///
+ /// Creates a none authentication method.
+ ///
+ ///
+ /// A none authentication method.
+ ///
IAuthenticationMethod IConnectionInfoInternal.CreateNoneAuthenticationMethod()
{
return new NoneAuthenticationMethod(Username);
}
+ ///
+ /// Gets the supported authentication methods for this connection.
+ ///
+ ///
+ /// The supported authentication methods for this connection.
+ ///
IList IConnectionInfoInternal.AuthenticationMethods
{
+#pragma warning disable S2365 // Properties should not make collection or array copies
get { return AuthenticationMethods.Cast().ToList(); }
+#pragma warning restore S2365 // Properties should not make collection or array copies
}
}
}
diff --git a/src/Renci.SshNet/ForwardedPortDynamic.cs b/src/Renci.SshNet/ForwardedPortDynamic.cs
index be67f6289..21ab59a20 100644
--- a/src/Renci.SshNet/ForwardedPortDynamic.cs
+++ b/src/Renci.SshNet/ForwardedPortDynamic.cs
@@ -361,10 +361,12 @@ private bool HandleSocks(IChannelDirectTcpip channel, Socket clientSocket, TimeS
Closing -= closeClientSocket;
}
- void closeClientSocket(object _, EventArgs args)
+#pragma warning disable SA1300 // Element should begin with upper-case letter
+ void closeClientSocket(object sender, EventArgs args)
{
CloseClientSocket(clientSocket);
- };
+ }
+#pragma warning restore SA1300 // Element should begin with upper-case letter
}
private static void CloseClientSocket(Socket clientSocket)
diff --git a/src/Renci.SshNet/ForwardedPortStatus.cs b/src/Renci.SshNet/ForwardedPortStatus.cs
index a8ab2224e..5695e28c7 100644
--- a/src/Renci.SshNet/ForwardedPortStatus.cs
+++ b/src/Renci.SshNet/ForwardedPortStatus.cs
@@ -34,7 +34,9 @@ public override bool Equals(object obj)
return forwardedPortStatus._value == _value;
}
+#pragma warning disable S3875 // "operator==" should not be overloaded on reference types
public static bool operator ==(ForwardedPortStatus left, ForwardedPortStatus right)
+#pragma warning restore S3875 // "operator==" should not be overloaded on reference types
{
// check if lhs is null
if (left is null)
diff --git a/src/Renci.SshNet/HashInfo.cs b/src/Renci.SshNet/HashInfo.cs
index 60829f1ed..cbbbf5fe7 100644
--- a/src/Renci.SshNet/HashInfo.cs
+++ b/src/Renci.SshNet/HashInfo.cs
@@ -5,7 +5,7 @@
namespace Renci.SshNet
{
///
- /// Holds information about key size and cipher to use
+ /// Holds information about key size and cipher to use.
///
public class HashInfo
{
@@ -23,7 +23,7 @@ public class HashInfo
public Func HashAlgorithm { get; private set; }
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// Size of the key.
/// The hash algorithm to use for a given key.
diff --git a/src/Renci.SshNet/IBaseClient.cs b/src/Renci.SshNet/IBaseClient.cs
index ff6d30bd3..add066f73 100644
--- a/src/Renci.SshNet/IBaseClient.cs
+++ b/src/Renci.SshNet/IBaseClient.cs
@@ -10,7 +10,7 @@ namespace Renci.SshNet
///
/// Serves as base class for client implementations, provides common client functionality.
///
- public interface IBaseClient
+ public interface IBaseClient : IDisposable
{
///
/// Gets the connection info.
@@ -87,11 +87,6 @@ public interface IBaseClient
/// The method was called after the client was disposed.
void Disconnect();
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- void Dispose();
-
///
/// Sends a keep-alive message to the server.
///
diff --git a/src/Renci.SshNet/IClientAuthentication.cs b/src/Renci.SshNet/IClientAuthentication.cs
index 5dbfccc3e..15124fc79 100644
--- a/src/Renci.SshNet/IClientAuthentication.cs
+++ b/src/Renci.SshNet/IClientAuthentication.cs
@@ -1,7 +1,23 @@
-namespace Renci.SshNet
+using System;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet
{
+ ///
+ /// Represents a mechanism to authenticate a given client.
+ ///
internal interface IClientAuthentication
{
+ ///
+ /// Attempts to perform authentication for a given using the
+ /// of the specified
+ /// .
+ ///
+ /// A to use for authenticating.
+ /// The for which to perform authentication.
+ /// or is .
+ /// Failed to Authenticate the client.
void Authenticate(IConnectionInfoInternal connectionInfo, ISession session);
}
}
diff --git a/src/Renci.SshNet/IConnectionInfo.cs b/src/Renci.SshNet/IConnectionInfo.cs
index 77908d2d7..a0f2898ef 100644
--- a/src/Renci.SshNet/IConnectionInfo.cs
+++ b/src/Renci.SshNet/IConnectionInfo.cs
@@ -3,39 +3,10 @@
using System.Text;
using Renci.SshNet.Common;
-using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages.Connection;
namespace Renci.SshNet
{
- internal interface IConnectionInfoInternal : IConnectionInfo
- {
- ///
- /// Signals that an authentication banner message was received from the server.
- ///
- /// The session in which the banner message was received.
- /// The banner message.
- void UserAuthenticationBannerReceived(object sender, MessageEventArgs e);
-
- ///
- /// Gets the supported authentication methods for this connection.
- ///
- ///
- /// The supported authentication methods for this connection.
- ///
- IList AuthenticationMethods { get; }
-
- ///
- /// Creates a for the credentials represented
- /// by the current .
- ///
- ///
- /// A for the credentials represented by the
- /// current .
- ///
- IAuthenticationMethod CreateNoneAuthenticationMethod();
- }
-
///
/// Represents remote connection information.
///
diff --git a/src/Renci.SshNet/IConnectionInfoInternal.cs b/src/Renci.SshNet/IConnectionInfoInternal.cs
new file mode 100644
index 000000000..053e36dda
--- /dev/null
+++ b/src/Renci.SshNet/IConnectionInfoInternal.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+using Renci.SshNet.Messages.Authentication;
+
+namespace Renci.SshNet
+{
+ ///
+ /// Represents remote connection information.
+ ///
+ internal interface IConnectionInfoInternal : IConnectionInfo
+ {
+ ///
+ /// Signals that an authentication banner message was received from the server.
+ ///
+ /// The session in which the banner message was received.
+ /// The banner message.
+ void UserAuthenticationBannerReceived(object sender, MessageEventArgs e);
+
+ ///
+ /// Gets the supported authentication methods for this connection.
+ ///
+ ///
+ /// The supported authentication methods for this connection.
+ ///
+ IList AuthenticationMethods { get; }
+
+ ///
+ /// Creates a for the credentials represented
+ /// by the current .
+ ///
+ ///
+ /// A for the credentials represented by the
+ /// current .
+ ///
+ IAuthenticationMethod CreateNoneAuthenticationMethod();
+ }
+}
diff --git a/src/Renci.SshNet/IForwardedPort.cs b/src/Renci.SshNet/IForwardedPort.cs
index 173613b38..92e49ee3b 100644
--- a/src/Renci.SshNet/IForwardedPort.cs
+++ b/src/Renci.SshNet/IForwardedPort.cs
@@ -1,7 +1,5 @@
using System;
-using Renci.SshNet.Common;
-
namespace Renci.SshNet
{
///
@@ -13,33 +11,5 @@ public interface IForwardedPort : IDisposable
/// The event occurs as the forwarded port is being stopped.
///
event EventHandler Closing;
-
- ///
- /// Occurs when an exception is thrown.
- ///
- event EventHandler Exception;
-
- ///
- /// Occurs when a port forwarding request is received.
- ///
- event EventHandler RequestReceived;
-
- ///
- /// Gets a value indicating whether port forwarding is started.
- ///
- ///
- /// if port forwarding is started; otherwise, .
- ///
- bool IsStarted { get; }
-
- ///
- /// Starts port forwarding.
- ///
- void Start();
-
- ///
- /// Stops port forwarding.
- ///
- void Stop();
}
}
diff --git a/src/Renci.SshNet/ISession.cs b/src/Renci.SshNet/ISession.cs
index fbac05708..5a035b104 100644
--- a/src/Renci.SshNet/ISession.cs
+++ b/src/Renci.SshNet/ISession.cs
@@ -17,7 +17,7 @@ namespace Renci.SshNet
internal interface ISession : IDisposable
{
///
- /// Gets or sets the connection info.
+ /// Gets the connection info.
///
/// The connection info.
IConnectionInfo ConnectionInfo { get; }
@@ -86,6 +86,9 @@ internal interface ISession : IDisposable
///
/// Creates a "forwarded-tcpip" SSH channel.
///
+ /// The number of the remote channel.
+ /// The window size of the remote channel.
+ /// The data packet size of the remote channel.
///
/// A new "forwarded-tcpip" SSH channel.
///
diff --git a/src/Renci.SshNet/ISftpClient.cs b/src/Renci.SshNet/ISftpClient.cs
index f9015f5a4..efbce934a 100644
--- a/src/Renci.SshNet/ISftpClient.cs
+++ b/src/Renci.SshNet/ISftpClient.cs
@@ -13,7 +13,7 @@ namespace Renci.SshNet
///
/// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
///
- public interface ISftpClient : IBaseClient, IDisposable
+ public interface ISftpClient : IBaseClient
{
///
/// Gets or sets the maximum size of the buffer in bytes.
@@ -78,12 +78,12 @@ public interface ISftpClient : IBaseClient, IDisposable
///
/// The file to append the lines to. The file is created if it does not already exist.
/// The lines to append to the file.
- /// is -or- is .
+ /// is . -or- is .
/// Client is not connected.
/// The specified path is invalid, or its directory was not found on the remote host.
/// The method was called after the client was disposed.
///
- /// The characters are written to the file using UTF-8 encoding without a Byte-Order Mark (BOM)
+ /// The characters are written to the file using UTF-8 encoding without a Byte-Order Mark (BOM).
///
void AppendAllLines(string path, IEnumerable contents);
@@ -239,7 +239,7 @@ public interface ISftpClient : IBaseClient, IDisposable
///
/// is .
/// is or contains only whitespace.
- /// If a problem occurs while copying the file
+ /// If a problem occurs while copying the file.
IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state);
///
@@ -568,7 +568,7 @@ public interface ISftpClient : IBaseClient, IDisposable
void EndUploadFile(IAsyncResult asyncResult);
///
- /// Checks whether file or directory exists;
+ /// Checks whether file or directory exists.
///
/// The path.
///
@@ -592,7 +592,9 @@ public interface ISftpClient : IBaseClient, IDisposable
/// was not found on the remote host.
/// is .
/// The method was called after the client was disposed.
+#pragma warning disable CA1716 // Identifiers should not match keywords
ISftpFile Get(string path);
+#pragma warning restore CA1716 // Identifiers should not match keywords
///
/// Gets the of the file on the path.
@@ -898,31 +900,31 @@ public interface ISftpClient : IBaseClient, IDisposable
void RenameFile(string oldPath, string newPath);
///
- /// Asynchronously renames remote file from old path to new path.
+ /// Renames remote file from old path to new path.
///
/// Path to the old file location.
/// Path to the new file location.
- /// The to observe.
- /// A that represents the asynchronous rename operation.
- /// is . -or- or is .
+ /// if set to then perform a posix rename.
+ /// is . -or- or is .
/// Client is not connected.
/// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server.
- /// A SSH error where is the message from the remote host.
+ /// A SSH error where is the message from the remote host.
/// The method was called after the client was disposed.
- Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken);
+ void RenameFile(string oldPath, string newPath, bool isPosix);
///
- /// Renames remote file from old path to new path.
+ /// Asynchronously renames remote file from old path to new path.
///
/// Path to the old file location.
/// Path to the new file location.
- /// if set to then perform a posix rename.
- /// is . -or- or is .
+ /// The to observe.
+ /// A that represents the asynchronous rename operation.
+ /// is . -or- or is .
/// Client is not connected.
/// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server.
- /// A SSH error where is the message from the remote host.
+ /// A SSH error where is the message from the remote host.
/// The method was called after the client was disposed.
- void RenameFile(string oldPath, string newPath, bool isPosix);
+ Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken);
///
/// Sets the date and time the specified file was last accessed.
@@ -951,7 +953,7 @@ public interface ISftpClient : IBaseClient, IDisposable
/// The file for which to set the date and time information.
/// A containing the value to set for the last write date and time of path. This value is expressed in UTC time.
void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc);
-
+
///
/// Sets the specified of the file on the specified path.
///
@@ -986,7 +988,7 @@ public interface ISftpClient : IBaseClient, IDisposable
/// is .
/// is or contains only whitespace.
/// was not found on the remote host.
- /// If a problem occurs while copying the file
+ /// If a problem occurs while copying the file.
IEnumerable SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern);
///
diff --git a/src/Renci.SshNet/ISubsystemSession.cs b/src/Renci.SshNet/ISubsystemSession.cs
index af1ce31e7..f1fe6f914 100644
--- a/src/Renci.SshNet/ISubsystemSession.cs
+++ b/src/Renci.SshNet/ISubsystemSession.cs
@@ -13,7 +13,7 @@ internal interface ISubsystemSession : IDisposable
/// Gets or set the number of seconds to wait for an operation to complete.
///
///
- /// The number of seconds to wait for an operation to complete, or -1 to wait indefinitely.
+ /// The number of seconds to wait for an operation to complete, or -1 to wait indefinitely.
///
int OperationTimeout { get; }
@@ -41,7 +41,7 @@ internal interface ISubsystemSession : IDisposable
/// Waits a specified time for a given to get signaled.
///
/// The handle to wait for.
- /// The number of millieseconds wait for to get signaled, or -1 to wait indefinitely.
+ /// The number of millieseconds wait for to get signaled, or -1 to wait indefinitely.
/// The connection was closed by the server.
/// The channel was closed.
/// The handle did not get signaled within the specified timeout.
@@ -52,7 +52,7 @@ internal interface ISubsystemSession : IDisposable
/// 32-bit signed integer to specify the time interval in milliseconds.
///
/// The handle to wait for.
- /// To number of milliseconds to wait for to get signaled, or -1 to wait indefinitely.
+ /// To number of milliseconds to wait for to get signaled, or -1 to wait indefinitely.
///
/// if received a signal within the specified timeout;
/// otherwise, .
@@ -72,7 +72,7 @@ internal interface ISubsystemSession : IDisposable
///
/// The first handle to wait for.
/// The second handle to wait for.
- /// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
+ /// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
///
/// 0 if received a signal within the specified timeout and 1
/// if received a signal within the specified timeout, or
@@ -98,7 +98,7 @@ internal interface ISubsystemSession : IDisposable
/// integer to specify the time interval.
///
/// A array - constructed using - containing the objects to wait for.
- /// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
+ /// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
///
/// The array index of the first non-system object that satisfied the wait.
///
diff --git a/src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs b/src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs
index 5c14bf80c..2d3825b3f 100644
--- a/src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs
+++ b/src/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs
@@ -134,12 +134,13 @@ public KeyboardInteractiveConnectionInfo(string host, int port, string username,
kbdInteractive.AuthenticationPrompt += AuthenticationMethod_AuthenticationPrompt;
}
}
-
}
private void AuthenticationMethod_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
{
+#pragma warning disable MA0091 // Sender should be 'this' for instance events
AuthenticationPrompt?.Invoke(sender, e);
+#pragma warning restore MA0091 // Sender should be 'this' for instance events
}
///
diff --git a/src/Renci.SshNet/Messages/Authentication/FailureMessage.cs b/src/Renci.SshNet/Messages/Authentication/FailureMessage.cs
index e3229287e..61379600f 100644
--- a/src/Renci.SshNet/Messages/Authentication/FailureMessage.cs
+++ b/src/Renci.SshNet/Messages/Authentication/FailureMessage.cs
@@ -25,7 +25,7 @@ public class FailureMessage : Message
/// Gets a value indicating whether authentication is partially successful.
///
///
- /// if partially successful; otherwise, .
+ /// if partially successful; otherwise, .
///
public bool PartialSuccess { get; private set; }
@@ -38,7 +38,11 @@ protected override void LoadData()
PartialSuccess = ReadBoolean();
if (PartialSuccess)
{
+#if NET || NETSTANDARD2_1_OR_GREATER
+ Message = string.Join(',', AllowedAuthentications);
+#else
Message = string.Join(",", AllowedAuthentications);
+#endif // NET || NETSTANDARD2_1_OR_GREATER
}
}
@@ -47,7 +51,9 @@ protected override void LoadData()
///
protected override void SaveData()
{
+#pragma warning disable MA0025 // Implement the functionality instead of throwing NotImplementedException
throw new NotImplementedException();
+#pragma warning restore MA0025 // Implement the functionality instead of throwing NotImplementedException
}
internal override void Process(Session session)
diff --git a/src/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs b/src/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs
index 4aa9cda41..9e3227342 100644
--- a/src/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs
+++ b/src/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs
@@ -12,7 +12,7 @@ internal sealed class InformationResponseMessage : Message
///
/// Gets authentication responses.
///
- public IList Responses { get; private set; }
+ public List Responses { get; private set; }
///
/// Gets the size of the message in bytes.
@@ -48,6 +48,7 @@ protected override void LoadData()
protected override void SaveData()
{
Write((uint) Responses.Count);
+
foreach (var response in Responses)
{
Write(response);
diff --git a/src/Renci.SshNet/Messages/Authentication/RequestMessage.cs b/src/Renci.SshNet/Messages/Authentication/RequestMessage.cs
index b535da224..5393df3f7 100644
--- a/src/Renci.SshNet/Messages/Authentication/RequestMessage.cs
+++ b/src/Renci.SshNet/Messages/Authentication/RequestMessage.cs
@@ -108,4 +108,3 @@ internal override void Process(Session session)
}
}
}
-
diff --git a/src/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs b/src/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs
index 1827c3299..1f84f4161 100644
--- a/src/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs
+++ b/src/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs
@@ -6,7 +6,7 @@
internal sealed class RequestMessageNone : RequestMessage
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// Name of the service.
/// Authentication username.
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelCloseMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelCloseMessage.cs
index 09a6ba834..dfb430134 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelCloseMessage.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelCloseMessage.cs
@@ -11,7 +11,6 @@ public class ChannelCloseMessage : ChannelMessage
///
public ChannelCloseMessage()
{
-
}
///
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelEofMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelEofMessage.cs
index 469b7b60b..0ad028242 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelEofMessage.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelEofMessage.cs
@@ -11,7 +11,6 @@ public class ChannelEofMessage : ChannelMessage
///
public ChannelEofMessage()
{
-
}
///
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelFailureMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelFailureMessage.cs
index f011f992a..5987534ca 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelFailureMessage.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelFailureMessage.cs
@@ -11,7 +11,6 @@ public class ChannelFailureMessage : ChannelMessage
///
public ChannelFailureMessage()
{
-
}
///
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenInfo.cs
index c9c334058..5c6e3c406 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenInfo.cs
@@ -3,7 +3,7 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Base class for open channel messages
+ /// Base class for open channel messages.
///
public abstract class ChannelOpenInfo : SshData
{
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs
index c2ac16c3e..aefb316ad 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs
@@ -3,7 +3,7 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Used to open "direct-tcpip" channel type
+ /// Used to open "direct-tcpip" channel type.
///
internal sealed class DirectTcpipChannelInfo : ChannelOpenInfo
{
@@ -11,7 +11,7 @@ internal sealed class DirectTcpipChannelInfo : ChannelOpenInfo
private byte[] _originatorAddress;
///
- /// Specifies channel open type
+ /// Specifies channel open type.
///
public const string NAME = "direct-tcpip";
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs
index b7d1efbf9..f8bb6742d 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs
@@ -3,10 +3,15 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Used to open "forwarded-tcpip" channel type
+ /// Used to open "forwarded-tcpip" channel type.
///
internal sealed class ForwardedTcpipChannelInfo : ChannelOpenInfo
{
+ ///
+ /// Specifies channel open type.
+ ///
+ public const string NAME = "forwarded-tcpip";
+
private byte[] _connectedAddress;
private byte[] _originatorAddress;
@@ -21,8 +26,8 @@ public ForwardedTcpipChannelInfo(byte[] data)
}
///
- /// Initializes a new instance with the specified connector
- /// address and port, and originator address and port.
+ /// Initializes a new instance of the class with the
+ /// specified connector address and port, and originator address and port.
///
public ForwardedTcpipChannelInfo(string connectedAddress, uint connectedPort, string originatorAddress, uint originatorPort)
{
@@ -32,11 +37,6 @@ public ForwardedTcpipChannelInfo(string connectedAddress, uint connectedPort, st
OriginatorPort = originatorPort;
}
- ///
- /// Specifies channel open type
- ///
- public const string NAME = "forwarded-tcpip";
-
///
/// Gets the type of the channel to open.
///
@@ -51,6 +51,9 @@ public override string ChannelType
///
/// Gets the connected address.
///
+ ///
+ /// The connected address.
+ ///
public string ConnectedAddress
{
get { return Utf8.GetString(_connectedAddress, 0, _connectedAddress.Length); }
@@ -60,11 +63,17 @@ public string ConnectedAddress
///
/// Gets the connected port.
///
+ ///
+ /// The connected port.
+ ///
public uint ConnectedPort { get; private set; }
///
/// Gets the originator address.
///
+ ///
+ /// The originator address.
+ ///
public string OriginatorAddress
{
get { return Utf8.GetString(_originatorAddress, 0, _originatorAddress.Length); }
@@ -74,6 +83,9 @@ public string OriginatorAddress
///
/// Gets the originator port.
///
+ ///
+ /// The originator port.
+ ///
public uint OriginatorPort { get; private set; }
///
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs
index c628b33d6..f20df5833 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs
@@ -3,12 +3,12 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Used to open "session" channel type
+ /// Used to open "session" channel type.
///
internal sealed class SessionChannelOpenInfo : ChannelOpenInfo
{
///
- /// Specifies channel open type
+ /// Specifies channel open type.
///
public const string Name = "session";
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs
index 4ae7237c4..d071799c1 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs
@@ -3,14 +3,14 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Used to open "x11" channel type
+ /// Used to open "x11" channel type.
///
internal sealed class X11ChannelOpenInfo : ChannelOpenInfo
{
private byte[] _originatorAddress;
///
- /// Specifies channel open type
+ /// Specifies channel open type.
///
public const string Name = "x11";
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpenFailureMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpenFailureMessage.cs
index 2a2888077..4488f9f5f 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelOpenFailureMessage.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelOpenFailureMessage.cs
@@ -62,7 +62,6 @@ protected override int BufferCapacity
///
public ChannelOpenFailureMessage()
{
-
}
///
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelOpenFailureReasons.cs b/src/Renci.SshNet/Messages/Connection/ChannelOpenFailureReasons.cs
index 94bbf8d33..0fbeaf4d3 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelOpenFailureReasons.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelOpenFailureReasons.cs
@@ -6,19 +6,22 @@
internal enum ChannelOpenFailureReasons : uint
{
///
- /// SSH_OPEN_ADMINISTRATIVELY_PROHIBITED
+ /// SSH_OPEN_ADMINISTRATIVELY_PROHIBITED.
///
AdministativelyProhibited = 1,
+
///
- /// SSH_OPEN_CONNECT_FAILED
+ /// SSH_OPEN_CONNECT_FAILED.
///
ConnectFailed = 2,
+
///
- /// SSH_OPEN_UNKNOWN_CHANNEL_TYPE
+ /// SSH_OPEN_UNKNOWN_CHANNEL_TYPE.
///
UnknownChannelType = 3,
+
///
- /// SSH_OPEN_RESOURCE_SHORTAGE
+ /// SSH_OPEN_RESOURCE_SHORTAGE.
///
ResourceShortage = 4
}
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs
index 472f8cb8e..c36655e84 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs
@@ -1,12 +1,12 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "break" type channel request information
+ /// Represents "break" type channel request information.
///
internal sealed class BreakRequestInfo : RequestInfo
{
///
- /// Channel request name
+ /// Channel request name.
///
public const string Name = "break";
@@ -43,7 +43,7 @@ protected override int BufferCapacity
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
public BreakRequestInfo()
{
@@ -51,7 +51,7 @@ public BreakRequestInfo()
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// Length of the break.
public BreakRequestInfo(uint breakLength)
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs
index ab4d99e96..fd9ad03b6 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs
@@ -17,7 +17,10 @@ public class ChannelRequestMessage : ChannelMessage
///
public string RequestName
{
- get { return _requestName; }
+ get
+ {
+ return _requestName;
+ }
private set
{
_requestName = value;
@@ -53,7 +56,7 @@ protected override int BufferCapacity
///
public ChannelRequestMessage()
{
- // Required for dynamically loading request type when it comes from the server
+ // Required for dynamically loading request type when it comes from the server
}
///
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs
index e5e3735a0..ac4fc0ebc 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs
@@ -1,12 +1,12 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "eow@openssh.com" type channel request information
+ /// Represents "eow@openssh.com" type channel request information.
///
public class EndOfWriteRequestInfo : RequestInfo
{
///
- /// Channel request name
+ /// Channel request name.
///
public const string Name = "eow@openssh.com";
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs
index 6901f3241..d04f80ef9 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "env" type channel request information
+ /// Represents "env" type channel request information.
///
internal sealed class EnvironmentVariableRequestInfo : RequestInfo
{
@@ -9,7 +9,7 @@ internal sealed class EnvironmentVariableRequestInfo : RequestInfo
private byte[] _variableValue;
///
- /// Channel request name
+ /// Channel request name.
///
public const string Name = "env";
@@ -25,7 +25,7 @@ public override string RequestName
}
///
- /// Gets or sets the name of the variable.
+ /// Gets the name of the variable.
///
///
/// The name of the variable.
@@ -36,7 +36,7 @@ public string VariableName
}
///
- /// Gets or sets the variable value.
+ /// Gets the value of the variable.
///
///
/// The variable value.
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs
index 9518b9e55..da17aee1d 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "exit-signal" type channel request information
+ /// Represents "exit-signal" type channel request information.
///
internal sealed class ExitSignalRequestInfo : RequestInfo
{
@@ -10,7 +10,7 @@ internal sealed class ExitSignalRequestInfo : RequestInfo
private byte[] _language;
///
- /// Channel request name
+ /// Channel request name.
///
public const string Name = "exit-signal";
@@ -41,7 +41,7 @@ public string SignalName
/// Gets a value indicating whether core is dumped.
///
///
- /// if core is dumped; otherwise, .
+ /// if core is dumped; otherwise, .
///
public bool CoreDumped { get; private set; }
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs
index 9aa92f96d..3d4651793 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "exit-status" type channel request information
+ /// Represents "exit-status" type channel request information.
///
internal sealed class ExitStatusRequestInfo : RequestInfo
{
@@ -30,7 +30,7 @@ protected override int BufferCapacity
{
get
{
- var capacity = base.BufferCapacity;
+ var capacity = base.BufferCapacity;
capacity += 4; // ExitStatus
return capacity;
}
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs
index 9bf303d6e..75b88df84 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs
@@ -1,12 +1,12 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "keepalive@openssh.com" type channel request information
+ /// Represents "keepalive@openssh.com" type channel request information.
///
public class KeepAliveRequestInfo : RequestInfo
{
///
- /// Channel request name
+ /// Channel request name.
///
public const string Name = "keepalive@openssh.com";
@@ -22,7 +22,7 @@ public override string RequestName
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
public KeepAliveRequestInfo()
{
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalRequestInfo.cs
similarity index 98%
rename from src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs
rename to src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalRequestInfo.cs
index fbe5a11dd..82df2712f 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalRequestInfo.cs
@@ -141,7 +141,7 @@ protected override void SaveData()
// write total length of encoded terminal modes, which is 1 bytes for the opcode / terminal mode
// and 4 bytes for the uint argument for each entry; the encoded terminal modes are terminated by
// opcode TTY_OP_END (which is 1 byte)
- Write((uint) TerminalModeValues.Count*(1 + 4) + 1);
+ Write(((uint) TerminalModeValues.Count*(1 + 4)) + 1);
foreach (var item in TerminalModeValues)
{
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/RequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/RequestInfo.cs
index 103e412b8..546f7ec6b 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/RequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/RequestInfo.cs
@@ -19,7 +19,7 @@ public abstract class RequestInfo : SshData
/// Gets or sets a value indicating whether reply message is needed.
///
///
- /// if reply message is needed; otherwise, .
+ /// if reply message is needed; otherwise, .
///
public bool WantReply { get; protected set; }
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs
index 42fc9cf1d..92ed4fd6b 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs
@@ -1,12 +1,12 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "shell" type channel request information
+ /// Represents "shell" type channel request information.
///
internal sealed class ShellRequestInfo : RequestInfo
{
///
- /// Channel request name
+ /// Channel request name.
///
public const string Name = "shell";
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs
index bb69e5ed2..c16c842c8 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "signal" type channel request information
+ /// Represents "signal" type channel request information.
///
internal sealed class SignalRequestInfo : RequestInfo
{
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs
index 6871b9fe3..0013792ca 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs
@@ -1,14 +1,14 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "subsystem" type channel request information
+ /// Represents "subsystem" type channel request information.
///
internal sealed class SubsystemRequestInfo : RequestInfo
{
private byte[] _subsystemName;
///
- /// Channel request name
+ /// Channel request name.
///
public const string Name = "subsystem";
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs
index edcd9af54..34e4c2cb3 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs
@@ -1,12 +1,12 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "window-change" type channel request information
+ /// Represents "window-change" type channel request information.
///
internal sealed class WindowChangeRequestInfo : RequestInfo
{
///
- /// Channe request name
+ /// Channe request name.
///
public const string Name = "window-change";
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs
index 794c1da53..c7f38c94f 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs
@@ -32,7 +32,7 @@ public override string RequestName
public bool IsSingleConnection { get; set; }
///
- /// Gets or sets the authentication protocol.
+ /// Gets the authentication protocol.
///
///
/// The authentication protocol.
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs b/src/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs
index 60924799f..2cf9a19c0 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Messages.Connection
{
///
- /// Represents "xon-xoff" type channel request information
+ /// Represents "xon-xoff" type channel request information.
///
internal sealed class XonXoffRequestInfo : RequestInfo
{
@@ -25,7 +25,7 @@ public override string RequestName
/// Gets or sets a value indicating whether client can do.
///
///
- /// if client can do; otherwise, .
+ /// if client can do; otherwise, .
///
public bool ClientCanDo { get; set; }
diff --git a/src/Renci.SshNet/Messages/Connection/ChannelSuccessMessage.cs b/src/Renci.SshNet/Messages/Connection/ChannelSuccessMessage.cs
index d6f21e9a5..50d302f21 100644
--- a/src/Renci.SshNet/Messages/Connection/ChannelSuccessMessage.cs
+++ b/src/Renci.SshNet/Messages/Connection/ChannelSuccessMessage.cs
@@ -11,7 +11,6 @@ public class ChannelSuccessMessage : ChannelMessage
///
public ChannelSuccessMessage()
{
-
}
///
diff --git a/src/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs b/src/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs
index cbd72795d..6e0ca3a21 100644
--- a/src/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs
+++ b/src/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs
@@ -23,7 +23,7 @@ public string RequestName
/// Gets a value indicating whether message reply should be sent..
///
///
- /// if message reply should be sent; otherwise, .
+ /// if message reply should be sent; otherwise, .
///
public bool WantReply { get; private set; }
diff --git a/src/Renci.SshNet/Messages/Connection/GlobalRequestName.cs b/src/Renci.SshNet/Messages/Connection/GlobalRequestName.cs
index 6a1d0e279..263278c0a 100644
--- a/src/Renci.SshNet/Messages/Connection/GlobalRequestName.cs
+++ b/src/Renci.SshNet/Messages/Connection/GlobalRequestName.cs
@@ -6,11 +6,12 @@
public enum GlobalRequestName
{
///
- /// tcpip-forward
+ /// tcpip-forward.
///
TcpIpForward,
+
///
- /// cancel-tcpip-forward
+ /// cancel-tcpip-forward.
///
CancelTcpIpForward,
}
diff --git a/src/Renci.SshNet/Messages/Message.cs b/src/Renci.SshNet/Messages/Message.cs
index d2fc3274a..ebe3a2e71 100644
--- a/src/Renci.SshNet/Messages/Message.cs
+++ b/src/Renci.SshNet/Messages/Message.cs
@@ -8,7 +8,7 @@
namespace Renci.SshNet.Messages
{
///
- /// Base class for all SSH protocol messages
+ /// Base class for all SSH protocol messages.
///
public abstract class Message : SshData
{
@@ -29,6 +29,7 @@ protected override int BufferCapacity
///
/// Writes the message to the specified .
///
+ /// The to write the message to.
protected override void WriteBytes(SshDataStream stream)
{
var enumerator = GetType().GetCustomAttributes(inherit: true).GetEnumerator();
@@ -55,58 +56,61 @@ internal byte[] GetPacket(byte paddingMultiplier, Compressor compressor)
var messageLength = BufferCapacity;
- SshDataStream sshDataStream;
-
if (messageLength == -1 || compressor != null)
{
- sshDataStream = new SshDataStream(DefaultCapacity);
+ using (var sshDataStream = new SshDataStream(DefaultCapacity))
+ {
+ // skip:
+ // * 4 bytes for the outbound packet sequence
+ // * 4 bytes for the packet data length
+ // * one byte for the packet padding length
+ _ = sshDataStream.Seek(outboundPacketSequenceSize + 4 + 1, SeekOrigin.Begin);
- // skip:
- // * 4 bytes for the outbound packet sequence
- // * 4 bytes for the packet data length
- // * one byte for the packet padding length
- _ = sshDataStream.Seek(outboundPacketSequenceSize + 4 + 1, SeekOrigin.Begin);
+ if (compressor != null)
+ {
+ // obtain uncompressed message payload
+ using (var uncompressedDataStream = new SshDataStream(messageLength != -1 ? messageLength : DefaultCapacity))
+ {
+ WriteBytes(uncompressedDataStream);
- if (compressor != null)
- {
- // obtain uncompressed message payload
- var uncompressedDataStream = new SshDataStream(messageLength != -1 ? messageLength : DefaultCapacity);
- WriteBytes(uncompressedDataStream);
+ // compress message payload
+ var compressedMessageData = compressor.Compress(uncompressedDataStream.ToArray());
- // compress message payload
- var compressedMessageData = compressor.Compress(uncompressedDataStream.ToArray());
+ // add compressed message payload
+ sshDataStream.Write(compressedMessageData, 0, compressedMessageData.Length);
+ }
+ }
+ else
+ {
+ // add message payload
+ WriteBytes(sshDataStream);
+ }
- // add compressed message payload
- sshDataStream.Write(compressedMessageData, 0, compressedMessageData.Length);
- }
- else
- {
- // add message payload
- WriteBytes(sshDataStream);
- }
+ messageLength = (int) sshDataStream.Length - (outboundPacketSequenceSize + 4 + 1);
- messageLength = (int) sshDataStream.Length - (outboundPacketSequenceSize + 4 + 1);
+ var packetLength = messageLength + 4 + 1;
- var packetLength = messageLength + 4 + 1;
+ // determine the padding length
+ var paddingLength = GetPaddingLength(paddingMultiplier, packetLength);
- // determine the padding length
- var paddingLength = GetPaddingLength(paddingMultiplier, packetLength);
+ // add padding bytes
+ var paddingBytes = new byte[paddingLength];
+ CryptoAbstraction.GenerateRandom(paddingBytes);
+ sshDataStream.Write(paddingBytes, 0, paddingLength);
- // add padding bytes
- var paddingBytes = new byte[paddingLength];
- CryptoAbstraction.GenerateRandom(paddingBytes);
- sshDataStream.Write(paddingBytes, 0, paddingLength);
+ var packetDataLength = GetPacketDataLength(messageLength, paddingLength);
- var packetDataLength = GetPacketDataLength(messageLength, paddingLength);
+ // skip bytes for outbound packet sequence
+ _ = sshDataStream.Seek(outboundPacketSequenceSize, SeekOrigin.Begin);
- // skip bytes for outbound packet sequence
- _ = sshDataStream.Seek(outboundPacketSequenceSize, SeekOrigin.Begin);
+ // add packet data length
+ sshDataStream.Write(packetDataLength);
- // add packet data length
- sshDataStream.Write(packetDataLength);
+ // add packet padding length
+ sshDataStream.WriteByte(paddingLength);
- // add packet padding length
- sshDataStream.WriteByte(paddingLength);
+ return sshDataStream.ToArray();
+ }
}
else
{
@@ -118,27 +122,28 @@ internal byte[] GetPacket(byte paddingMultiplier, Compressor compressor)
var packetDataLength = GetPacketDataLength(messageLength, paddingLength);
// lets construct an SSH data stream of the exact size required
- sshDataStream = new SshDataStream(packetLength + paddingLength + outboundPacketSequenceSize);
+ using (var sshDataStream = new SshDataStream(packetLength + paddingLength + outboundPacketSequenceSize))
+ {
+ // skip bytes for outbound packet sequenceSize
+ _ = sshDataStream.Seek(outboundPacketSequenceSize, SeekOrigin.Begin);
- // skip bytes for outbound packet sequenceSize
- _ = sshDataStream.Seek(outboundPacketSequenceSize, SeekOrigin.Begin);
+ // add packet data length
+ sshDataStream.Write(packetDataLength);
- // add packet data length
- sshDataStream.Write(packetDataLength);
+ // add packet padding length
+ sshDataStream.WriteByte(paddingLength);
- // add packet padding length
- sshDataStream.WriteByte(paddingLength);
+ // add message payload
+ WriteBytes(sshDataStream);
- // add message payload
- WriteBytes(sshDataStream);
+ // add padding bytes
+ var paddingBytes = new byte[paddingLength];
+ CryptoAbstraction.GenerateRandom(paddingBytes);
+ sshDataStream.Write(paddingBytes, 0, paddingLength);
- // add padding bytes
- var paddingBytes = new byte[paddingLength];
- CryptoAbstraction.GenerateRandom(paddingBytes);
- sshDataStream.Write(paddingBytes, 0, paddingLength);
+ return sshDataStream.ToArray();
+ }
}
-
- return sshDataStream.ToArray();
}
private static uint GetPacketDataLength(int messageLength, byte paddingLength)
diff --git a/src/Renci.SshNet/Messages/MessageAttribute.cs b/src/Renci.SshNet/Messages/MessageAttribute.cs
index 10c0cdef0..f94d13c32 100644
--- a/src/Renci.SshNet/Messages/MessageAttribute.cs
+++ b/src/Renci.SshNet/Messages/MessageAttribute.cs
@@ -2,7 +2,6 @@
namespace Renci.SshNet.Messages
{
-
///
/// Indicates that a class represents SSH message. This class cannot be inherited.
///
@@ -10,20 +9,20 @@ namespace Renci.SshNet.Messages
public sealed class MessageAttribute : Attribute
{
///
- /// Gets or sets message name as defined in RFC 4250.
+ /// Gets the message name as defined in RFC 4250.
///
///
/// The name.
///
- public string Name { get; set; }
+ public string Name { get; }
///
- /// Gets or sets message number as defined in RFC 4250.
+ /// Gets the message number as defined in RFC 4250.
///
///
/// The number.
///
- public byte Number { get; set; }
+ public byte Number { get; }
///
/// Initializes a new instance of the class.
diff --git a/src/Renci.SshNet/Messages/ServiceName.cs b/src/Renci.SshNet/Messages/ServiceName.cs
index 1c63ddbed..bfd6644cd 100644
--- a/src/Renci.SshNet/Messages/ServiceName.cs
+++ b/src/Renci.SshNet/Messages/ServiceName.cs
@@ -1,17 +1,17 @@
namespace Renci.SshNet.Messages
{
///
- /// Specifies list of supported services
+ /// Specifies list of supported services.
///
public enum ServiceName
{
///
- /// ssh-userauth
+ /// ssh-userauth.
///
UserAuthentication,
///
- /// ssh-connection
+ /// ssh-connection.
///
Connection
}
diff --git a/src/Renci.SshNet/Messages/Transport/DebugMessage.cs b/src/Renci.SshNet/Messages/Transport/DebugMessage.cs
index b7347283f..f550d95fe 100644
--- a/src/Renci.SshNet/Messages/Transport/DebugMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/DebugMessage.cs
@@ -13,7 +13,7 @@ public class DebugMessage : Message
/// Gets a value indicating whether the message to be always displayed.
///
///
- /// if the message always to be displayed; otherwise, .
+ /// if the message always to be displayed; otherwise, .
///
public bool IsAlwaysDisplay { get; private set; }
diff --git a/src/Renci.SshNet/Messages/Transport/DisconnectReason.cs b/src/Renci.SshNet/Messages/Transport/DisconnectReason.cs
index 35c6f885a..cf4119ba9 100644
--- a/src/Renci.SshNet/Messages/Transport/DisconnectReason.cs
+++ b/src/Renci.SshNet/Messages/Transport/DisconnectReason.cs
@@ -9,65 +9,82 @@ public enum DisconnectReason
/// Disconnect reason is not provided.
///
None = 0,
+
///
- /// SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT
+ /// SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT.
///
HostNotAllowedToConnect = 1,
+
///
- /// SSH_DISCONNECT_PROTOCOL_ERROR
+ /// SSH_DISCONNECT_PROTOCOL_ERROR.
///
ProtocolError = 2,
+
///
- /// SSH_DISCONNECT_KEY_EXCHANGE_FAILED
+ /// SSH_DISCONNECT_KEY_EXCHANGE_FAILED.
///
KeyExchangeFailed = 3,
+
///
- /// SSH_DISCONNECT_RESERVED
+ /// SSH_DISCONNECT_RESERVED.
///
+#pragma warning disable CA1700 // Do not name enum values 'Reserved'
Reserved = 4,
+#pragma warning restore CA1700 // Do not name enum values 'Reserved'
+
///
- /// SSH_DISCONNECT_MAC_ERROR
+ /// SSH_DISCONNECT_MAC_ERROR.
///
MacError = 5,
+
///
- /// SSH_DISCONNECT_COMPRESSION_ERROR
+ /// SSH_DISCONNECT_COMPRESSION_ERROR.
///
CompressionError = 6,
+
///
- /// SSH_DISCONNECT_SERVICE_NOT_AVAILABLE
+ /// SSH_DISCONNECT_SERVICE_NOT_AVAILABLE.
///
ServiceNotAvailable = 7,
+
///
- /// SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED
+ /// SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED.
///
ProtocolVersionNotSupported = 8,
+
///
- /// SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE
+ /// SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE.
///
HostKeyNotVerifiable = 9,
+
///
- /// SSH_DISCONNECT_CONNECTION_LOST
+ /// SSH_DISCONNECT_CONNECTION_LOST.
///
ConnectionLost = 10,
+
///
- /// SSH_DISCONNECT_BY_APPLICATION
+ /// SSH_DISCONNECT_BY_APPLICATION.
///
ByApplication = 11,
+
///
- /// SSH_DISCONNECT_TOO_MANY_CONNECTIONS
+ /// SSH_DISCONNECT_TOO_MANY_CONNECTIONS.
///
TooManyConnections = 12,
+
///
- /// SSH_DISCONNECT_AUTH_CANCELLED_BY_USER
+ /// SSH_DISCONNECT_AUTH_CANCELLED_BY_USER.
///
AuthenticationCanceledByUser = 13,
+
///
- /// SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE
+ /// SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE.
///
NoMoreAuthenticationMethodsAvailable = 14,
+
///
- /// SSH_DISCONNECT_ILLEGAL_USER_NAME
+ /// SSH_DISCONNECT_ILLEGAL_USER_NAME.
///
- IllegalUserName = 15,
+ IllegalUserName = 15
}
}
diff --git a/src/Renci.SshNet/Messages/Transport/IKeyExchangedAllowed.cs b/src/Renci.SshNet/Messages/Transport/IKeyExchangedAllowed.cs
index b53a09e40..1f06aeb31 100644
--- a/src/Renci.SshNet/Messages/Transport/IKeyExchangedAllowed.cs
+++ b/src/Renci.SshNet/Messages/Transport/IKeyExchangedAllowed.cs
@@ -1,9 +1,11 @@
namespace Renci.SshNet.Messages.Transport
{
///
- /// Indicates that message that implement this interface is allowed during key exchange phase
+ /// Indicates that message that implement this interface is allowed during key exchange phase.
///
+#pragma warning disable CA1040 // Avoid empty interfaces
public interface IKeyExchangedAllowed
+#pragma warning restore CA1040 // Avoid empty interfaces
{
}
}
diff --git a/src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs b/src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs
index 5fc009a37..5d07f1d00 100644
--- a/src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs
@@ -18,13 +18,27 @@ public class IgnoreMessage : Message
public byte[] Data { get; private set; }
///
- /// Initializes a new instance of the class
+ /// Initializes a new instance of the class.
///
public IgnoreMessage()
{
Data = Array.Empty();
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The data.
+ public IgnoreMessage(byte[] data)
+ {
+ if (data is null)
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
+ Data = data;
+ }
+
///
/// Gets the size of the message in bytes.
///
@@ -42,20 +56,6 @@ protected override int BufferCapacity
}
}
- ///
- /// Initializes a new instance of the class.
- ///
- /// The data.
- public IgnoreMessage(byte[] data)
- {
- if (data is null)
- {
- throw new ArgumentNullException(nameof(data));
- }
-
- Data = data;
- }
-
///
/// Called when type specific data need to be loaded.
///
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs
index 6fe1c76ff..f1010a19d 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs
@@ -14,7 +14,7 @@ public class KeyExchangeDhGroupExchangeGroup : Message
private byte[] _subGroup;
///
- /// Gets or sets the safe prime.
+ /// Gets the safe prime.
///
///
/// The safe prime.
@@ -25,7 +25,7 @@ public BigInteger SafePrime
}
///
- /// Gets or sets the generator for subgroup in GF(p).
+ /// Gets the generator for subgroup in GF(p).
///
///
/// The sub group.
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs
index 105e5797c..8f0a1489f 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs
@@ -7,7 +7,7 @@
public class KeyExchangeDhReplyMessage : Message
{
///
- /// Gets server public host key and certificates
+ /// Gets server public host key and certificates.
///
/// The host key.
public byte[] HostKey { get; private set; }
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs
index 4bd508f54..2d02d1480 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs
@@ -10,7 +10,7 @@ namespace Renci.SshNet.Messages.Transport
internal sealed class KeyExchangeEcdhInitMessage : Message, IKeyExchangedAllowed
{
///
- /// Gets the client's ephemeral contribution to the ECDH exchange, encoded as an octet string
+ /// Gets the client's ephemeral contribution to the ECDH exchange, encoded as an octet string.
///
public byte[] QC { get; private set; }
diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs
index a194caf1e..a5626aece 100644
--- a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs
+++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs
@@ -7,7 +7,7 @@
public class KeyExchangeEcdhReplyMessage : Message
{
///
- /// Gets a string encoding an X.509v3 certificate containing the server's ECDSA public host key
+ /// Gets a string encoding an X.509v3 certificate containing the server's ECDSA public host key.
///
/// The host key.
public byte[] KS { get; private set; }
@@ -69,4 +69,4 @@ internal override void Process(Session session)
session.OnKeyExchangeEcdhReplyMessageReceived(this);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet/Netconf/INetConfSession.cs b/src/Renci.SshNet/Netconf/INetConfSession.cs
index 4dd3e24d4..223cd3e0d 100644
--- a/src/Renci.SshNet/Netconf/INetConfSession.cs
+++ b/src/Renci.SshNet/Netconf/INetConfSession.cs
@@ -1,25 +1,39 @@
using System.Xml;
+using Renci.SshNet.Common;
+
namespace Renci.SshNet.NetConf
{
+ ///
+ /// Represents a NETCONF session.
+ ///
internal interface INetConfSession : ISubsystemSession
{
///
- /// Gets the NetConf server capabilities.
+ /// Gets the NETCONF server capabilities.
///
///
- /// The NetConf server capabilities.
+ /// The NETCONF server capabilities.
///
XmlDocument ServerCapabilities { get; }
///
- /// Gets the NetConf client capabilities.
+ /// Gets the NETCONF client capabilities.
///
///
- /// The NetConf client capabilities.
+ /// The NETCONF client capabilities.
///
XmlDocument ClientCapabilities { get; }
+ ///
+ /// Sends the specified RPC request and returns the reply sent by the NETCONF server.
+ ///
+ /// The RPC request.
+ /// to automatically increment the message id and verify the message id of the RPC reply.
+ ///
+ /// The RPC reply.
+ ///
+ /// is and the message id in the RPC reply does not match the message id of the RPC request.
XmlDocument SendReceiveRpc(XmlDocument rpc, bool automaticMessageIdHandling);
}
}
diff --git a/src/Renci.SshNet/Netconf/NetConfSession.cs b/src/Renci.SshNet/Netconf/NetConfSession.cs
index f1252f539..317b99c1c 100644
--- a/src/Renci.SshNet/Netconf/NetConfSession.cs
+++ b/src/Renci.SshNet/Netconf/NetConfSession.cs
@@ -156,7 +156,11 @@ protected override void OnDataReceived(byte[] data)
position += match.Index + match.Length + fractionLength;
}
+#if NET7_0_OR_GREATER
+ if (Regex.IsMatch(chunk.AsSpan(position), @"\n##\n"))
+#else
if (Regex.IsMatch(chunk.Substring(position), @"\n##\n"))
+#endif // NET7_0_OR_GREATER
{
_ = _rpcReplyReceived.Set();
}
diff --git a/src/Renci.SshNet/PasswordAuthenticationMethod.cs b/src/Renci.SshNet/PasswordAuthenticationMethod.cs
index d6e6a78a6..f53016240 100644
--- a/src/Renci.SshNet/PasswordAuthenticationMethod.cs
+++ b/src/Renci.SshNet/PasswordAuthenticationMethod.cs
@@ -105,7 +105,7 @@ public override AuthenticationResult Authenticate(Session session)
session.SendMessage(_requestMessage);
session.WaitOnHandle(_authenticationCompleted);
}
- finally
+ finally
{
session.UnRegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ");
session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived;
diff --git a/src/Renci.SshNet/PasswordConnectionInfo.cs b/src/Renci.SshNet/PasswordConnectionInfo.cs
index eb2ad9230..08ef59dea 100644
--- a/src/Renci.SshNet/PasswordConnectionInfo.cs
+++ b/src/Renci.SshNet/PasswordConnectionInfo.cs
@@ -6,7 +6,7 @@
namespace Renci.SshNet
{
///
- /// Provides connection information when password authentication method is used
+ /// Provides connection information when password authentication method is used.
///
///
///
@@ -260,7 +260,9 @@ public PasswordConnectionInfo(string host, int port, string username, byte[] pas
private void AuthenticationMethod_PasswordExpired(object sender, AuthenticationPasswordChangeEventArgs e)
{
+#pragma warning disable MA0091 // Sender should be 'this' for instance events
PasswordExpired?.Invoke(sender, e);
+#pragma warning restore MA0091 // Sender should be 'this' for instance events
}
///
diff --git a/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs b/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
index 7acf40ce2..a8f7f4bd3 100644
--- a/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
+++ b/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
@@ -17,7 +17,9 @@ public class PrivateKeyAuthenticationMethod : AuthenticationMethod, IDisposable
{
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure;
private EventWaitHandle _authenticationCompleted = new ManualResetEvent(initialState: false);
+#pragma warning disable S1450 // Private fields only used as local variables in methods should become local variables
private bool _isSignatureRequired;
+#pragma warning restore S1450 // Private fields only used as local variables in methods should become local variables
private bool _isDisposed;
///
@@ -168,7 +170,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs
index e5a3ffa3d..eb7b3767b 100644
--- a/src/Renci.SshNet/PrivateKeyFile.cs
+++ b/src/Renci.SshNet/PrivateKeyFile.cs
@@ -68,14 +68,14 @@ namespace Renci.SshNet
public class PrivateKeyFile : IPrivateKeySource, IDisposable
{
private static readonly Regex PrivateKeyRegex = new Regex(@"^-+ *BEGIN (?\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k PRIVATE KEY *-+",
- RegexOptions.Compiled | RegexOptions.Multiline);
+ RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ExplicitCapture);
private readonly List _hostAlgorithms = new List();
private Key _key;
private bool _isDisposed;
///
- /// The supported host algorithms for this key file.
+ /// Gets the supported host algorithms for this key file.
///
public IReadOnlyCollection HostKeyAlgorithms
{
@@ -235,7 +235,7 @@ private void Open(Stream privateKey, string passPhrase)
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
break;
default:
- throw new SshException(string.Format(CultureInfo.CurrentCulture, "Private key cipher \"{0}\" is not supported.", cipherName));
+ throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key cipher \"{0}\" is not supported.", cipherName));
}
decryptedData = DecryptKey(cipher, binaryData, passPhrase, binarySalt);
@@ -250,8 +250,10 @@ private void Open(Stream privateKey, string passPhrase)
case "RSA":
var rsaKey = new RsaKey(decryptedData);
_key = rsaKey;
+#pragma warning disable CA2000 // Dispose objects before losing scope
_hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA512)));
_hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256)));
+#pragma warning restore CA2000 // Dispose objects before losing scope
_hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key));
break;
case "DSA":
@@ -266,14 +268,17 @@ private void Open(Stream privateKey, string passPhrase)
_key = ParseOpenSshV1Key(decryptedData, passPhrase);
if (_key is RsaKey parsedRsaKey)
{
+#pragma warning disable CA2000 // Dispose objects before losing scope
_hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(parsedRsaKey, HashAlgorithmName.SHA512)));
_hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(parsedRsaKey, HashAlgorithmName.SHA256)));
+#pragma warning restore CA2000 // Dispose objects before losing scope
_hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key));
}
else
{
_hostAlgorithms.Add(new KeyHostAlgorithm(_key.ToString(), _key));
}
+
break;
case "SSH2 ENCRYPTED":
var reader = new SshDataReader(decryptedData);
@@ -309,7 +314,9 @@ private void Open(Stream privateKey, string passPhrase)
throw new SshException(string.Format("Cipher method '{0}' is not supported.", cipherName));
}
- // TODO: Create two specific data types to avoid using SshDataReader class
+ /*
+ * TODO: Create two specific data types to avoid using SshDataReader class.
+ */
reader = new SshDataReader(keyData);
@@ -330,8 +337,10 @@ private void Open(Stream privateKey, string passPhrase)
var p = reader.ReadBigIntWithBits(); // q
var decryptedRsaKey = new RsaKey(modulus, exponent, d, p, q, inverseQ);
_key = decryptedRsaKey;
+#pragma warning disable CA2000 // Dispose objects before losing scope
_hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(decryptedRsaKey, HashAlgorithmName.SHA512)));
_hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(decryptedRsaKey, HashAlgorithmName.SHA256)));
+#pragma warning restore CA2000 // Dispose objects before losing scope
_hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key));
}
else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}")
@@ -341,6 +350,7 @@ private void Open(Stream privateKey, string passPhrase)
{
throw new SshException("Invalid private key");
}
+
var p = reader.ReadBigIntWithBits();
var g = reader.ReadBigIntWithBits();
var q = reader.ReadBigIntWithBits();
@@ -353,6 +363,7 @@ private void Open(Stream privateKey, string passPhrase)
{
throw new NotSupportedException(string.Format("Key type '{0}' is not supported.", keyType));
}
+
break;
default:
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Key '{0}' is not supported.", keyName));
@@ -436,8 +447,8 @@ private static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, strin
/// Parses an OpenSSH V1 key file (i.e. ED25519 key) according to the the key spec:
/// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
///
- /// the key file data (i.e. base64 encoded data between the header/footer)
- /// passphrase or null if there isn't one
+ /// The key file data (i.e. base64 encoded data between the header/footer).
+ /// Passphrase or if there isn't one.
///
/// The OpenSSH V1 key.
///
@@ -540,7 +551,10 @@ private static Key ParseOpenSshV1Key(byte[] keyFileData, string passPhrase)
var checkInt2 = (int) privateKeyReader.ReadUInt32();
if (checkInt1 != checkInt2)
{
- throw new SshException("The random check bytes of the OpenSSH key do not match (" + checkInt1 + " <->" + checkInt2 + ").");
+ throw new SshException(string.Format(CultureInfo.InvariantCulture,
+ "The random check bytes of the OpenSSH key do not match ({0} <-> {1}).",
+ checkInt1.ToString(CultureInfo.InvariantCulture),
+ checkInt2.ToString(CultureInfo.InvariantCulture)));
}
// key type
@@ -595,7 +609,8 @@ private static Key ParseOpenSshV1Key(byte[] keyFileData, string passPhrase)
{
if ((int) padding[i] != i + 1)
{
- throw new SshException("Padding of openssh key format contained wrong byte at position: " + i);
+ throw new SshException("Padding of openssh key format contained wrong byte at position: " +
+ i.ToString(CultureInfo.InvariantCulture));
}
}
diff --git a/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs b/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs
index d99338feb..7af4c6381 100644
--- a/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs
+++ b/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs
@@ -14,12 +14,11 @@
[assembly: AssemblyInformationalVersion("2023.0.0")]
[assembly: CLSCompliant(false)]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
-
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
diff --git a/src/Renci.SshNet/ProxyTypes.cs b/src/Renci.SshNet/ProxyTypes.cs
index 9a7fce098..cff5eb247 100644
--- a/src/Renci.SshNet/ProxyTypes.cs
+++ b/src/Renci.SshNet/ProxyTypes.cs
@@ -5,13 +5,24 @@
///
public enum ProxyTypes
{
- /// No proxy server.
+ ///
+ /// No proxy server.
+ ///
None,
- /// A SOCKS4 proxy server.
+
+ ///
+ /// A SOCKS4 proxy server.
+ ///
Socks4,
- /// A SOCKS5 proxy server.
+
+ ///
+ /// A SOCKS5 proxy server.
+ ///
Socks5,
- /// A HTTP proxy server.
+
+ ///
+ /// An HTTP proxy server.
+ ///
Http,
}
}
diff --git a/src/Renci.SshNet/RemotePathTransformation.cs b/src/Renci.SshNet/RemotePathTransformation.cs
index 5e3f74fcb..7404c3e4b 100644
--- a/src/Renci.SshNet/RemotePathTransformation.cs
+++ b/src/Renci.SshNet/RemotePathTransformation.cs
@@ -27,7 +27,7 @@ public static class RemotePathTransformation
private static readonly IRemotePathTransformation DoubleQuoteTransformation = new RemotePathDoubleQuoteTransformation();
///
- /// Quotes a path in a way to be suitable to be used with a shell-based server.
+ /// Gets a that quotes a path in a way to be suitable to be used with a shell-based server.
///
///
/// A quoted path.
@@ -85,7 +85,7 @@ public static IRemotePathTransformation ShellQuote
}
///
- /// Performs no transformation.
+ /// Gets a that performs no transformation.
///
///
/// Recommended for servers that do not require any character to be escaped or enclosed in quotes,
@@ -97,7 +97,7 @@ public static IRemotePathTransformation None
}
///
- /// Encloses a path in double quotes, and escapes any embedded double quote with a backslash.
+ /// Gets a that encloses a path in double quotes, and escapes any embedded double quote with a backslash.
///
///
/// A transformation that encloses a path in double quotes, and escapes any embedded double quote with
diff --git a/src/Renci.SshNet/ScpClient.cs b/src/Renci.SshNet/ScpClient.cs
index 1413a12d9..8121c9702 100644
--- a/src/Renci.SshNet/ScpClient.cs
+++ b/src/Renci.SshNet/ScpClient.cs
@@ -632,7 +632,11 @@ private string ReadString(Stream stream)
/// The file or directory to upload.
private void UploadTimes(IChannelSession channel, Stream input, FileSystemInfo fileOrDirectory)
{
+#if NET ||NETSTANDARD2_1_OR_GREATER
+ var zeroTime = DateTime.UnixEpoch;
+#else
var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+#endif
var modificationSeconds = (long) (fileOrDirectory.LastWriteTimeUtc - zeroTime).TotalSeconds;
var accessSeconds = (long) (fileOrDirectory.LastAccessTimeUtc - zeroTime).TotalSeconds;
SendData(channel, string.Format(CultureInfo.InvariantCulture, "T{0} 0 {1} 0\n", modificationSeconds, accessSeconds));
@@ -805,7 +809,11 @@ private void InternalDownload(IChannelSession channel, Stream input, FileSystemI
var mtime = long.Parse(match.Result("${mtime}"), CultureInfo.InvariantCulture);
var atime = long.Parse(match.Result("${atime}"), CultureInfo.InvariantCulture);
+#if NET || NETSTANDARD2_1_OR_GREATER
+ var zeroTime = DateTime.UnixEpoch;
+#else
var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+#endif
modifiedTime = zeroTime.AddSeconds(mtime);
accessedTime = zeroTime.AddSeconds(atime);
continue;
diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs b/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs
index 635a25409..a98959efe 100644
--- a/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs
+++ b/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs
@@ -648,7 +648,9 @@ public BigInteger(
int nBytes = GetByteLength(sizeInBits);
byte[] b = new byte[nBytes];
+#pragma warning disable CA5394 // Do not use insecure randomness
random.NextBytes(b);
+#pragma warning restore CA5394 // Do not use insecure randomness
// strip off any excess bits in the MSB
int xBits = BitsPerByte * nBytes - sizeInBits;
@@ -658,6 +660,7 @@ public BigInteger(
this.sign = this.magnitude.Length < 1 ? 0 : 1;
}
+#pragma warning disable CA5394 // Do not use insecure randomness
public BigInteger(
int bitLength,
int certainty,
@@ -716,6 +719,7 @@ public BigInteger(
}
}
}
+#pragma warning restore CA5394 // Do not use insecure randomness
public BigInteger Abs()
{
diff --git a/src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs b/src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs
index 377817e01..6e86c47ca 100644
--- a/src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs
+++ b/src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs
@@ -58,7 +58,9 @@ public SecureRandom()
///
/// The source to generate all random bytes from.
public SecureRandom(IRandomGenerator generator)
+#pragma warning disable CA5394 // Do not use insecure randomness
: base(0)
+#pragma warning restore CA5394 // Do not use insecure randomness
{
this.generator = generator;
}
diff --git a/src/Renci.SshNet/Security/CertificateHostAlgorithm.cs b/src/Renci.SshNet/Security/CertificateHostAlgorithm.cs
index 6110e3215..41d5f4103 100644
--- a/src/Renci.SshNet/Security/CertificateHostAlgorithm.cs
+++ b/src/Renci.SshNet/Security/CertificateHostAlgorithm.cs
@@ -29,7 +29,7 @@ public CertificateHostAlgorithm(string name)
///
/// The data.
/// Signed data.
- ///
+ /// Always.
public override byte[] Sign(byte[] data)
{
throw new NotImplementedException();
@@ -41,7 +41,7 @@ public override byte[] Sign(byte[] data)
/// The data.
/// The signature.
/// if signature was successfully verified; otherwise .
- ///
+ /// Always.
public override bool VerifySignature(byte[] data, byte[] signature)
{
throw new NotImplementedException();
diff --git a/src/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs b/src/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs
index 91d0c77ff..74e4a733b 100644
--- a/src/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs
@@ -3,7 +3,7 @@
///
/// Base class for asymmetric cipher implementations.
///
- public abstract class AsymmetricCipher : Cipher
+ public abstract class AsymmetricCipher : Cipher
{
///
/// Gets the minimum data size.
diff --git a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs
index 00c66f6c1..b9f7dde58 100644
--- a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs
@@ -69,7 +69,9 @@ protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding
/// The data.
/// The zero-based offset in at which to begin encrypting.
/// The number of bytes to encrypt from .
- /// Encrypted data
+ ///
+ /// The encrypted data.
+ ///
public override byte[] Encrypt(byte[] input, int offset, int length)
{
if (length % _blockSize > 0)
@@ -112,7 +114,9 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
/// Decrypts the specified data.
///
/// The data.
- /// Decrypted data
+ ///
+ /// The decrypted data.
+ ///
public override byte[] Decrypt(byte[] input)
{
return Decrypt(input, 0, input.Length);
diff --git a/src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs
index ed37e4346..e61b5d655 100644
--- a/src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs
+++ b/src/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs
@@ -4,7 +4,7 @@
namespace Renci.SshNet.Security.Cryptography
{
///
- /// Implements digital signature where where asymmetric cipher is used,
+ /// Implements digital signature where where asymmetric cipher is used.
///
public abstract class CipherDigitalSignature : DigitalSignature
{
@@ -33,7 +33,7 @@ protected CipherDigitalSignature(ObjectIdentifier oid, AsymmetricCipher cipher)
/// The input.
/// The signature.
///
- /// if signature was successfully verified; otherwise .
+ /// if signature was successfully verified; otherwise .
///
public override bool Verify(byte[] input, byte[] signature)
{
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs
index 09e8ab90c..517f2b463 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs
@@ -17,7 +17,10 @@ public sealed class AesCipher : BlockCipher
private int _rounds;
private uint[] _encryptionKey;
private uint[] _decryptionKey;
- private uint _c0, _c1, _c2, _c3;
+ private uint _c0;
+ private uint _c1;
+ private uint _c2;
+ private uint _c3;
#region Static Definition Tables
@@ -583,7 +586,7 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding)
/// The number of bytes encrypted.
///
/// or is .
- /// or is too short.
+ /// or is too short.
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
if (inputBuffer is null)
@@ -598,12 +601,12 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC
if ((inputOffset + (32 / 2)) > inputBuffer.Length)
{
- throw new IndexOutOfRangeException("input buffer too short");
+ throw new ArgumentException("input buffer too short");
}
if ((outputOffset + (32 / 2)) > outputBuffer.Length)
{
- throw new IndexOutOfRangeException("output buffer too short");
+ throw new ArgumentException("output buffer too short");
}
_encryptionKey ??= GenerateWorkingKey(isEncryption: true, Key);
@@ -644,12 +647,12 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
if ((inputOffset + (32 / 2)) > inputBuffer.Length)
{
- throw new IndexOutOfRangeException("input buffer too short");
+ throw new ArgumentException("input buffer too short");
}
if ((outputOffset + (32 / 2)) > outputBuffer.Length)
{
- throw new IndexOutOfRangeException("output buffer too short");
+ throw new ArgumentException("output buffer too short");
}
_decryptionKey ??= GenerateWorkingKey(isEncryption: false, Key);
@@ -675,26 +678,26 @@ private uint[] GenerateWorkingKey(bool isEncryption, byte[] key)
_rounds = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
var W = new uint[(_rounds + 1) * 4]; // 4 words in a block
- //
- // copy the key into the round key array
- //
+ /*
+ * Copy the key into the round key array.
+ */
var t = 0;
for (var i = 0; i < key.Length; t++)
{
- W[(t >> 2) * 4 + (t & 3)] = Pack.LittleEndianToUInt32(key, i);
+ W[((t >> 2) * 4) + (t & 3)] = Pack.LittleEndianToUInt32(key, i);
i += 4;
}
- //
- // while not enough round key material calculated
- // calculate new values
- //
+ /*
+ * Calculate new values while not enough round key material is calculated.
+ */
+
var k = (_rounds + 1) << 2;
for (var i = KC; i < k; i++)
{
- var temp = W[((i - 1) >> 2) * 4 + ((i - 1) & 3)];
+ var temp = W[(((i - 1) >> 2) * 4) + ((i - 1) & 3)];
if ((i % KC) == 0)
{
temp = SubWord(Shift(temp, 8)) ^ Rcon[(i / KC) - 1];
@@ -704,7 +707,7 @@ private uint[] GenerateWorkingKey(bool isEncryption, byte[] key)
temp = SubWord(temp);
}
- W[(i >> 2) * 4 + (i & 3)] = W[((i - KC) >> 2) * 4 + ((i - KC) & 3)] ^ temp;
+ W[((i >> 2) * 4) + (i & 3)] = W[(((i - KC) >> 2) * 4) + ((i - KC) & 3)] ^ temp;
}
if (!isEncryption)
@@ -713,7 +716,7 @@ private uint[] GenerateWorkingKey(bool isEncryption, byte[] key)
{
for (var i = 0; i < 4; i++)
{
- W[j * 4 + i] = InvMcol(W[j * 4 + i]);
+ W[(j * 4) + i] = InvMcol(W[(j * 4) + i]);
}
}
}
@@ -765,74 +768,82 @@ private void PackBlock(byte[] bytes, int off)
Pack.UInt32ToLittleEndian(_c3, bytes, off + 12);
}
+#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
private void EncryptBlock(uint[] KW)
+#pragma warning restore SA1313 // Parameter names should begin with lower-case letter
{
int r;
uint r0, r1, r2, r3;
- _c0 ^= KW[0 * 4 + 0];
- _c1 ^= KW[0 * 4 + 1];
- _c2 ^= KW[0 * 4 + 2];
- _c3 ^= KW[0 * 4 + 3];
+ _c0 ^= KW[(0 * 4) + 0];
+ _c1 ^= KW[(0 * 4) + 1];
+ _c2 ^= KW[(0 * 4) + 2];
+ _c3 ^= KW[(0 * 4) + 3];
for (r = 1; r < _rounds - 1;)
{
- r0 = T0[_c0 & 255] ^ T1[(_c1 >> 8) & 255] ^ T2[(_c2 >> 16) & 255] ^ T3[_c3 >> 24] ^ KW[r * 4 + 0];
- r1 = T0[_c1 & 255] ^ T1[(_c2 >> 8) & 255] ^ T2[(_c3 >> 16) & 255] ^ T3[_c0 >> 24] ^ KW[r * 4 + 1];
- r2 = T0[_c2 & 255] ^ T1[(_c3 >> 8) & 255] ^ T2[(_c0 >> 16) & 255] ^ T3[_c1 >> 24] ^ KW[r * 4 + 2];
- r3 = T0[_c3 & 255] ^ T1[(_c0 >> 8) & 255] ^ T2[(_c1 >> 16) & 255] ^ T3[_c2 >> 24] ^ KW[r++ * 4 + 3];
- _c0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ KW[r * 4 + 0];
- _c1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ KW[r * 4 + 1];
- _c2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ KW[r * 4 + 2];
- _c3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ KW[r++ * 4 + 3];
+ r0 = T0[_c0 & 255] ^ T1[(_c1 >> 8) & 255] ^ T2[(_c2 >> 16) & 255] ^ T3[_c3 >> 24] ^ KW[(r * 4) + 0];
+ r1 = T0[_c1 & 255] ^ T1[(_c2 >> 8) & 255] ^ T2[(_c3 >> 16) & 255] ^ T3[_c0 >> 24] ^ KW[(r * 4) + 1];
+ r2 = T0[_c2 & 255] ^ T1[(_c3 >> 8) & 255] ^ T2[(_c0 >> 16) & 255] ^ T3[_c1 >> 24] ^ KW[(r * 4) + 2];
+ r3 = T0[_c3 & 255] ^ T1[(_c0 >> 8) & 255] ^ T2[(_c1 >> 16) & 255] ^ T3[_c2 >> 24] ^ KW[(r++ * 4) + 3];
+ _c0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ KW[(r * 4) + 0];
+ _c1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ KW[(r * 4) + 1];
+ _c2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ KW[(r * 4) + 2];
+ _c3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ KW[(r++ * 4) + 3];
}
- r0 = T0[_c0 & 255] ^ T1[(_c1 >> 8) & 255] ^ T2[(_c2 >> 16) & 255] ^ T3[_c3 >> 24] ^ KW[r * 4 + 0];
- r1 = T0[_c1 & 255] ^ T1[(_c2 >> 8) & 255] ^ T2[(_c3 >> 16) & 255] ^ T3[_c0 >> 24] ^ KW[r * 4 + 1];
- r2 = T0[_c2 & 255] ^ T1[(_c3 >> 8) & 255] ^ T2[(_c0 >> 16) & 255] ^ T3[_c1 >> 24] ^ KW[r * 4 + 2];
- r3 = T0[_c3 & 255] ^ T1[(_c0 >> 8) & 255] ^ T2[(_c1 >> 16) & 255] ^ T3[_c2 >> 24] ^ KW[r++ * 4 + 3];
+ r0 = T0[_c0 & 255] ^ T1[(_c1 >> 8) & 255] ^ T2[(_c2 >> 16) & 255] ^ T3[_c3 >> 24] ^ KW[(r * 4) + 0];
+ r1 = T0[_c1 & 255] ^ T1[(_c2 >> 8) & 255] ^ T2[(_c3 >> 16) & 255] ^ T3[_c0 >> 24] ^ KW[(r * 4) + 1];
+ r2 = T0[_c2 & 255] ^ T1[(_c3 >> 8) & 255] ^ T2[(_c0 >> 16) & 255] ^ T3[_c1 >> 24] ^ KW[(r * 4) + 2];
+ r3 = T0[_c3 & 255] ^ T1[(_c0 >> 8) & 255] ^ T2[(_c1 >> 16) & 255] ^ T3[_c2 >> 24] ^ KW[(r++ * 4) + 3];
- // the final round's table is a simple function of S so we don't use a whole other four tables for it
+ /*
+ * The final round's table is a simple function of S so we don't use a whole other four tables for it.
+ */
- _c0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ KW[r * 4 + 0];
- _c1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ KW[r * 4 + 1];
- _c2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ KW[r * 4 + 2];
- _c3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ KW[r * 4 + 3];
+ _c0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ KW[(r * 4) + 0];
+ _c1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ KW[(r * 4) + 1];
+ _c2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ KW[(r * 4) + 2];
+ _c3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ KW[(r * 4) + 3];
}
+#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
private void DecryptBlock(uint[] KW)
+#pragma warning restore SA1313 // Parameter names should begin with lower-case letter
{
int r;
uint r0, r1, r2, r3;
- _c0 ^= KW[_rounds * 4 + 0];
- _c1 ^= KW[_rounds * 4 + 1];
- _c2 ^= KW[_rounds * 4 + 2];
- _c3 ^= KW[_rounds * 4 + 3];
+ _c0 ^= KW[(_rounds * 4) + 0];
+ _c1 ^= KW[(_rounds * 4) + 1];
+ _c2 ^= KW[(_rounds * 4) + 2];
+ _c3 ^= KW[(_rounds * 4) + 3];
for (r = _rounds - 1; r > 1;)
{
- r0 = Tinv0[_c0 & 255] ^ Tinv1[(_c3 >> 8) & 255] ^ Tinv2[(_c2 >> 16) & 255] ^ Tinv3[_c1 >> 24] ^ KW[r * 4 + 0];
- r1 = Tinv0[_c1 & 255] ^ Tinv1[(_c0 >> 8) & 255] ^ Tinv2[(_c3 >> 16) & 255] ^ Tinv3[_c2 >> 24] ^ KW[r * 4 + 1];
- r2 = Tinv0[_c2 & 255] ^ Tinv1[(_c1 >> 8) & 255] ^ Tinv2[(_c0 >> 16) & 255] ^ Tinv3[_c3 >> 24] ^ KW[r * 4 + 2];
- r3 = Tinv0[_c3 & 255] ^ Tinv1[(_c2 >> 8) & 255] ^ Tinv2[(_c1 >> 16) & 255] ^ Tinv3[_c0 >> 24] ^ KW[r-- * 4 + 3];
- _c0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ KW[r * 4 + 0];
- _c1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ KW[r * 4 + 1];
- _c2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ KW[r * 4 + 2];
- _c3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ KW[r-- * 4 + 3];
+ r0 = Tinv0[_c0 & 255] ^ Tinv1[(_c3 >> 8) & 255] ^ Tinv2[(_c2 >> 16) & 255] ^ Tinv3[_c1 >> 24] ^ KW[(r * 4) + 0];
+ r1 = Tinv0[_c1 & 255] ^ Tinv1[(_c0 >> 8) & 255] ^ Tinv2[(_c3 >> 16) & 255] ^ Tinv3[_c2 >> 24] ^ KW[(r * 4) + 1];
+ r2 = Tinv0[_c2 & 255] ^ Tinv1[(_c1 >> 8) & 255] ^ Tinv2[(_c0 >> 16) & 255] ^ Tinv3[_c3 >> 24] ^ KW[(r * 4) + 2];
+ r3 = Tinv0[_c3 & 255] ^ Tinv1[(_c2 >> 8) & 255] ^ Tinv2[(_c1 >> 16) & 255] ^ Tinv3[_c0 >> 24] ^ KW[(r-- * 4) + 3];
+ _c0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ KW[(r * 4) + 0];
+ _c1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ KW[(r * 4) + 1];
+ _c2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ KW[(r * 4) + 2];
+ _c3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ KW[(r-- * 4) + 3];
}
- r0 = Tinv0[_c0 & 255] ^ Tinv1[(_c3 >> 8) & 255] ^ Tinv2[(_c2 >> 16) & 255] ^ Tinv3[_c1 >> 24] ^ KW[r * 4 + 0];
- r1 = Tinv0[_c1 & 255] ^ Tinv1[(_c0 >> 8) & 255] ^ Tinv2[(_c3 >> 16) & 255] ^ Tinv3[_c2 >> 24] ^ KW[r * 4 + 1];
- r2 = Tinv0[_c2 & 255] ^ Tinv1[(_c1 >> 8) & 255] ^ Tinv2[(_c0 >> 16) & 255] ^ Tinv3[_c3 >> 24] ^ KW[r * 4 + 2];
- r3 = Tinv0[_c3 & 255] ^ Tinv1[(_c2 >> 8) & 255] ^ Tinv2[(_c1 >> 16) & 255] ^ Tinv3[_c0 >> 24] ^ KW[r * 4 + 3];
+ r0 = Tinv0[_c0 & 255] ^ Tinv1[(_c3 >> 8) & 255] ^ Tinv2[(_c2 >> 16) & 255] ^ Tinv3[_c1 >> 24] ^ KW[(r * 4) + 0];
+ r1 = Tinv0[_c1 & 255] ^ Tinv1[(_c0 >> 8) & 255] ^ Tinv2[(_c3 >> 16) & 255] ^ Tinv3[_c2 >> 24] ^ KW[(r * 4) + 1];
+ r2 = Tinv0[_c2 & 255] ^ Tinv1[(_c1 >> 8) & 255] ^ Tinv2[(_c0 >> 16) & 255] ^ Tinv3[_c3 >> 24] ^ KW[(r * 4) + 2];
+ r3 = Tinv0[_c3 & 255] ^ Tinv1[(_c2 >> 8) & 255] ^ Tinv2[(_c1 >> 16) & 255] ^ Tinv3[_c0 >> 24] ^ KW[(r * 4) + 3];
- // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+ /*
+ * The final round's table is a simple function of Si so we don't use a whole other four tables for it.
+ */
- _c0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ KW[0 * 4 + 0];
- _c1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ KW[0 * 4 + 1];
- _c2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ KW[0 * 4 + 2];
- _c3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ KW[0 * 4 + 3];
+ _c0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ KW[(0 * 4) + 0];
+ _c1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ KW[(0 * 4) + 1];
+ _c2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ KW[(0 * 4) + 2];
+ _c3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ KW[(0 * 4) + 3];
}
}
}
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs
index 5bfbd011e..41387ee02 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs
@@ -3,14 +3,16 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
///
- /// Implements ARCH4 cipher algorithm
+ /// Implements ARCH4 cipher algorithm.
///
public sealed class Arc4Cipher : StreamCipher
{
- private static readonly int STATE_LENGTH = 256;
+#pragma warning disable SA1310 // Field names should not contain underscore
+ private const int STATE_LENGTH = 256;
+#pragma warning restore SA1310 // Field names should not contain underscore
///
- /// Holds the state of the RC4 engine
+ /// Holds the state of the RC4 engine.
///
private byte[] _engineState;
@@ -119,21 +121,19 @@ public override byte[] Decrypt(byte[] input)
///
public override byte[] Decrypt(byte[] input, int offset, int length)
{
- var output = new byte[length];
- _ = ProcessBytes(input, offset, length, output, 0);
- return output;
+ return Encrypt(input, offset, length);
}
private int ProcessBytes(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
if ((inputOffset + inputCount) > inputBuffer.Length)
{
- throw new IndexOutOfRangeException("input buffer too short");
+ throw new ArgumentException("input buffer too short");
}
if ((outputOffset + inputCount) > outputBuffer.Length)
{
- throw new IndexOutOfRangeException("output buffer too short");
+ throw new ArgumentException("output buffer too short");
}
for (var i = 0; i < inputCount; i++)
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs
index b2e082058..6e04c999c 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs
@@ -8,6 +8,12 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
///
public sealed class BlowfishCipher : BlockCipher
{
+ private const int Rounds = 16;
+
+ private const int SboxSk = 256;
+
+ private const int PSize = Rounds + 2;
+
#region Static reference tables
private static readonly uint[] KP =
@@ -293,19 +299,16 @@ public sealed class BlowfishCipher : BlockCipher
#endregion
- private const int Rounds = 16;
-
- private const int SboxSk = 256;
-
- private const int PSize = Rounds + 2;
-
///
- /// The s-boxes
+ /// The s-boxes.
///
- private readonly uint[] _s0, _s1, _s2, _s3;
+ private readonly uint[] _s0;
+ private readonly uint[] _s1;
+ private readonly uint[] _s2;
+ private readonly uint[] _s3;
///
- /// The p-array
+ /// The p-array.
///
private readonly uint[] _p;
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs
index fa114bfeb..d23887856 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs
@@ -4,21 +4,21 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
///
- /// Implements CAST cipher algorithm
+ /// Implements CAST cipher algorithm.
///
public sealed class CastCipher : BlockCipher
{
- private static readonly int MaxRounds = 16;
+ private const int MaxRounds = 16;
- private static readonly int RedRounds = 12;
+ private const int RedRounds = 12;
///
- /// The rotating round key
+ /// The rotating round key.
///
private readonly int[] _kr = new int[17];
///
- /// The masking round key
+ /// The masking round key.
///
private readonly uint[] _km = new uint[17];
@@ -58,9 +58,11 @@ public CastCipher(byte[] key, CipherMode mode, CipherPadding padding)
///
public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
- // process the input block
- // batch the units up into a 32 bit chunk and go for it
- // the array is in bytes, the increment is 8x8 bits = 64
+ /*
+ * process the input block
+ * batch the units up into a 32 bit chunk and go for it
+ * the array is in bytes, the increment is 8x8 bits = 64
+ */
var l0 = Pack.BigEndianToUInt32(inputBuffer, inputOffset);
var r0 = Pack.BigEndianToUInt32(inputBuffer, inputOffset + 4);
@@ -575,7 +577,6 @@ private void SetKey(byte[] key)
/// The input to be processed.
/// The mask to be used from Km[n].
/// The rotation value to be used.
- ///
private static uint F1(uint d, uint kmi, int kri)
{
var I = kmi + d;
@@ -589,7 +590,6 @@ private static uint F1(uint d, uint kmi, int kri)
/// The input to be processed.
/// The mask to be used from Km[n].
/// The rotation value to be used.
- ///
private static uint F2(uint d, uint kmi, int kri)
{
var I = kmi ^ d;
@@ -603,7 +603,6 @@ private static uint F2(uint d, uint kmi, int kri)
/// The input to be processed.
/// The mask to be used from Km[n].
/// The rotation value to be used.
- ///
private static uint F3(uint d, uint kmi, int kri)
{
var I = kmi - d;
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs
index 7352a12f0..490756aff 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs
@@ -3,10 +3,12 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
///
- /// Base class for cipher mode implementations
+ /// Base class for cipher mode implementations.
///
public abstract class CipherMode
{
+#pragma warning disable SA1401 // Fields should be private
+#pragma warning disable SA1306 // Field names should begin with lower-case letter
///
/// Gets the cipher.
///
@@ -21,6 +23,8 @@ public abstract class CipherMode
/// Holds block size of the cipher.
///
protected int _blockSize;
+#pragma warning restore SA1306 // Field names should begin with lower-case letter
+#pragma warning restore SA1401 // Fields should be private
///
/// Initializes a new instance of the class.
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs
index 5013865cb..914babc11 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
///
- /// Base class for cipher padding implementations
+ /// Base class for cipher padding implementations.
///
public abstract class CipherPadding
{
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs
index aac593534..f2d2e4035 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs
@@ -12,7 +12,7 @@ public class DesCipher : BlockCipher
private int[] _decryptionKey;
- private static readonly short[] Bytebit = {128, 64, 32, 16, 8, 4, 2, 1};
+ private static readonly short[] Bytebit = { 128, 64, 32, 16, 8, 4, 2, 1 };
private static readonly int[] Bigbyte =
{
@@ -237,12 +237,12 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC
{
if ((inputOffset + BlockSize) > inputBuffer.Length)
{
- throw new IndexOutOfRangeException("input buffer too short");
+ throw new ArgumentException("input buffer too short");
}
if ((outputOffset + BlockSize) > outputBuffer.Length)
{
- throw new IndexOutOfRangeException("output buffer too short");
+ throw new ArgumentException("output buffer too short");
}
_encryptionKey ??= GenerateWorkingKey(encrypting: true, Key);
@@ -267,12 +267,12 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
{
if ((inputOffset + BlockSize) > inputBuffer.Length)
{
- throw new IndexOutOfRangeException("input buffer too short");
+ throw new ArgumentException("input buffer too short");
}
if ((outputOffset + BlockSize) > outputBuffer.Length)
{
- throw new IndexOutOfRangeException("output buffer too short");
+ throw new ArgumentException("output buffer too short");
}
_decryptionKey ??= GenerateWorkingKey(encrypting: false, Key);
@@ -300,7 +300,7 @@ protected int[] GenerateWorkingKey(bool encrypting, byte[] key)
{
int l = Pc1[j];
- pc1m[j] = ((key[(uint)l >> 3] & Bytebit[l & 07]) != 0);
+ pc1m[j] = (key[(uint) l >> 3] & Bytebit[l & 07]) != 0;
}
for (var i = 0; i < 16; i++)
@@ -359,9 +359,10 @@ protected int[] GenerateWorkingKey(bool encrypting, byte[] key)
}
}
- //
- // store the processed key
- //
+ /*
+ * store the processed key
+ */
+
for (var i = 0; i != 32; i += 2)
{
var i1 = newKey[i];
@@ -428,24 +429,24 @@ protected static void DesFunc(int[] wKey, byte[] input, int inOff, byte[] outByt
for (var round = 0; round < 8; round++)
{
work = (right << 28) | (right >> 4);
- work ^= (uint)wKey[round * 4 + 0];
+ work ^= (uint)wKey[(round * 4) + 0];
var fval = Sp7[work & 0x3f];
fval |= Sp5[(work >> 8) & 0x3f];
fval |= Sp3[(work >> 16) & 0x3f];
fval |= Sp1[(work >> 24) & 0x3f];
- work = right ^ (uint) wKey[round * 4 + 1];
+ work = right ^ (uint) wKey[(round * 4) + 1];
fval |= Sp8[work & 0x3f];
fval |= Sp6[(work >> 8) & 0x3f];
fval |= Sp4[(work >> 16) & 0x3f];
fval |= Sp2[(work >> 24) & 0x3f];
left ^= fval;
work = (left << 28) | (left >> 4);
- work ^= (uint)wKey[round * 4 + 2];
+ work ^= (uint)wKey[(round * 4) + 2];
fval = Sp7[work & 0x3f];
fval |= Sp5[(work >> 8) & 0x3f];
fval |= Sp3[(work >> 16) & 0x3f];
fval |= Sp1[(work >> 24) & 0x3f];
- work = left ^ (uint)wKey[round * 4 + 3];
+ work = left ^ (uint)wKey[(round * 4) + 3];
fval |= Sp8[work & 0x3f];
fval |= Sp6[(work >> 8) & 0x3f];
fval |= Sp4[(work >> 16) & 0x3f];
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs
index de07141c3..a2b9243d4 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs
@@ -4,7 +4,7 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes
{
///
- /// Implements CBC cipher mode
+ /// Implements CBC cipher mode.
///
public class CbcCipherMode : CipherMode
{
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs
index ae889e875..23a4bb2f7 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs
@@ -4,7 +4,7 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes
{
///
- /// Implements CFB cipher mode
+ /// Implements CFB cipher mode.
///
public class CfbCipherMode : CipherMode
{
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs
index 90149f575..a0ae5010b 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs
@@ -4,7 +4,7 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes
{
///
- /// Implements CTR cipher mode
+ /// Implements CTR cipher mode.
///
public class CtrCipherMode : CipherMode
{
@@ -77,35 +77,7 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC
///
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
- if (inputBuffer.Length - inputOffset < _blockSize)
- {
- throw new ArgumentException("Invalid input buffer");
- }
-
- if (outputBuffer.Length - outputOffset < _blockSize)
- {
- throw new ArgumentException("Invalid output buffer");
- }
-
- if (inputCount != _blockSize)
- {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize));
- }
-
- _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0);
-
- for (var i = 0; i < _blockSize; i++)
- {
- outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]);
- }
-
- var j = IV.Length;
- while (--j >= 0 && ++IV[j] == 0)
- {
- // Intentionally empty block
- }
-
- return _blockSize;
+ return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
}
}
}
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs
index 1f321f454..e87dc9d32 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs
@@ -4,7 +4,7 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes
{
///
- /// Implements OFB cipher mode
+ /// Implements OFB cipher mode.
///
public class OfbCipherMode : CipherMode
{
@@ -74,32 +74,7 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC
///
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
- if (inputBuffer.Length - inputOffset < _blockSize)
- {
- throw new ArgumentException("Invalid input buffer");
- }
-
- if (outputBuffer.Length - outputOffset < _blockSize)
- {
- throw new ArgumentException("Invalid output buffer");
- }
-
- if (inputCount != _blockSize)
- {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize));
- }
-
- _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0);
-
- for (var i = 0; i < _blockSize; i++)
- {
- outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]);
- }
-
- Buffer.BlockCopy(IV, _blockSize, IV, 0, IV.Length - _blockSize);
- Buffer.BlockCopy(outputBuffer, outputOffset, IV, IV.Length - _blockSize, _blockSize);
-
- return _blockSize;
+ return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
}
}
}
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs
index cfe0ae9a3..eb950abf4 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs
@@ -3,7 +3,7 @@
namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings
{
///
- /// Implements PKCS7 cipher padding
+ /// Implements PKCS7 cipher padding.
///
public class PKCS7Padding : CipherPadding
{
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs
index 749083c03..8cb58a93e 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs
@@ -36,7 +36,7 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
// Calculate signature
var bitLength = _key.Modulus.BitLength;
- var paddedBlock = new byte[bitLength / 8 + (bitLength % 8 > 0 ? 1 : 0) - 1];
+ var paddedBlock = new byte[(bitLength / 8) + (bitLength % 8 > 0 ? 1 : 0) - 1];
paddedBlock[0] = 0x01;
for (var i = 1; i < paddedBlock.Length - length - 1; i++)
@@ -141,7 +141,7 @@ private byte[] Transform(byte[] data, int offset, int length)
var h = BigInteger.PositiveMod((mP - mQ) * _key.InverseQ, _key.P);
- var m = h * _key.Q + mQ;
+ var m = (h * _key.Q) + mQ;
var rInv = BigInteger.ModInverse(random, _key.Modulus);
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs
index c8032b00b..6f19e176d 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs
@@ -871,7 +871,7 @@ private void Sb3(int a, int b, int c, int d)
}
///
- /// InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms
+ /// InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms.
///
/// A.
/// The b.
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs
index eef40c0e6..eb1c0839d 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs
@@ -41,12 +41,12 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC
{
if ((inputOffset + BlockSize) > inputBuffer.Length)
{
- throw new IndexOutOfRangeException("input buffer too short");
+ throw new ArgumentException("input buffer too short");
}
if ((outputOffset + BlockSize) > outputBuffer.Length)
{
- throw new IndexOutOfRangeException("output buffer too short");
+ throw new ArgumentException("output buffer too short");
}
if (_encryptionKey1 is null || _encryptionKey2 is null || _encryptionKey3 is null)
@@ -97,12 +97,12 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
{
if ((inputOffset + BlockSize) > inputBuffer.Length)
{
- throw new IndexOutOfRangeException("input buffer too short");
+ throw new ArgumentException("input buffer too short");
}
if ((outputOffset + BlockSize) > outputBuffer.Length)
{
- throw new IndexOutOfRangeException("output buffer too short");
+ throw new ArgumentException("output buffer too short");
}
if (_decryptionKey1 is null || _decryptionKey2 is null || _decryptionKey3 is null)
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs
index 8ecf6793a..7ef039cc3 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs
@@ -7,6 +7,72 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
///
public sealed class TwofishCipher : BlockCipher
{
+ /**
+ * Define the fixed p0/p1 permutations used in keyed S-box lookup.
+ * By changing the following constant definitions, the S-boxes will
+ * automatically Get changed in the Twofish engine.
+ */
+#pragma warning disable SA1310 // Field names should not contain underscore
+ private const int P_00 = 1;
+ private const int P_01 = 0;
+ private const int P_02 = 0;
+ private const int P_03 = P_01 ^ 1;
+ private const int P_04 = 1;
+
+ private const int P_10 = 0;
+ private const int P_11 = 0;
+ private const int P_12 = 1;
+ private const int P_13 = P_11 ^ 1;
+ private const int P_14 = 0;
+
+ private const int P_20 = 1;
+ private const int P_21 = 1;
+ private const int P_22 = 0;
+ private const int P_23 = P_21 ^ 1;
+ private const int P_24 = 0;
+
+ private const int P_30 = 0;
+ private const int P_31 = 1;
+ private const int P_32 = 1;
+ private const int P_33 = P_31 ^ 1;
+ private const int P_34 = 1;
+
+ /* Primitive polynomial for GF(256) */
+ private const int GF256_FDBK = 0x169;
+ private const int GF256_FDBK_2 = GF256_FDBK / 2;
+ private const int GF256_FDBK_4 = GF256_FDBK / 4;
+
+ private const int RS_GF_FDBK = 0x14D; // field generator
+
+ private const int ROUNDS = 16;
+ private const int MAX_ROUNDS = 16; // bytes = 128 bits
+ private const int MAX_KEY_BITS = 256;
+
+ private const int INPUT_WHITEN = 0;
+ private const int OUTPUT_WHITEN = INPUT_WHITEN + (16 / 4); // 4
+ private const int ROUND_SUBKEYS = OUTPUT_WHITEN + (16 / 4); // 8
+
+ private const int TOTAL_SUBKEYS = ROUND_SUBKEYS + (2 * MAX_ROUNDS); // 40
+
+ private const int SK_STEP = 0x02020202;
+ private const int SK_BUMP = 0x01010101;
+ private const int SK_ROTL = 9;
+#pragma warning restore SA1310 // Field names should not contain underscore
+
+ private readonly int[] _gMDS0 = new int[MAX_KEY_BITS];
+ private readonly int[] _gMDS1 = new int[MAX_KEY_BITS];
+ private readonly int[] _gMDS2 = new int[MAX_KEY_BITS];
+ private readonly int[] _gMDS3 = new int[MAX_KEY_BITS];
+
+ private readonly int _k64Cnt;
+
+ /*
+ * _gSubKeys[] and _gSBox[] are eventually used in the
+ * encryption and decryption methods.
+ */
+ private int[] _gSubKeys;
+ private int[] _gSBox;
+
///
/// Initializes a new instance of the class.
///
@@ -80,13 +146,13 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC
var t1 = Fe32_3(_gSBox, x1);
x2 ^= t0 + t1 + _gSubKeys[k++];
x2 = (int)((uint)x2 >> 1) | x2 << 31;
- x3 = (x3 << 1 | (int)((uint)x3 >> 31)) ^ (t0 + 2 * t1 + _gSubKeys[k++]);
+ x3 = (x3 << 1 | (int)((uint)x3 >> 31)) ^ (t0 + (2 * t1) + _gSubKeys[k++]);
t0 = Fe32_0(_gSBox, x2);
t1 = Fe32_3(_gSBox, x3);
x0 ^= t0 + t1 + _gSubKeys[k++];
x0 = (int)((uint)x0 >> 1) | x0 << 31;
- x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + 2 * t1 + _gSubKeys[k++]);
+ x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + (2 * t1) + _gSubKeys[k++]);
}
Bits32ToBytes(x2 ^ _gSubKeys[OUTPUT_WHITEN], outputBuffer, outputOffset);
@@ -115,20 +181,20 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
var x0 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ _gSubKeys[OUTPUT_WHITEN + 2];
var x1 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ _gSubKeys[OUTPUT_WHITEN + 3];
- var k = ROUND_SUBKEYS + 2 * ROUNDS - 1;
+ var k = ROUND_SUBKEYS + (2 * ROUNDS) - 1;
for (var r = 0; r < ROUNDS; r += 2)
{
var t0 = Fe32_0(_gSBox, x2);
var t1 = Fe32_3(_gSBox, x3);
- x1 ^= t0 + 2 * t1 + _gSubKeys[k--];
- x0 = (x0 << 1 | (int)((uint)x0 >> 31)) ^ (t0 + t1 + _gSubKeys[k--]);
- x1 = (int)((uint)x1 >> 1) | x1 << 31;
+ x1 ^= t0 + (2 * t1) + _gSubKeys[k--];
+ x0 = (x0 << 1 | (int) ((uint) x0 >> 31)) ^ (t0 + t1 + _gSubKeys[k--]);
+ x1 = (int) ((uint) x1 >> 1) | x1 << 31;
t0 = Fe32_0(_gSBox, x0);
t1 = Fe32_3(_gSBox, x1);
- x3 ^= t0 + 2 * t1 + _gSubKeys[k--];
- x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + _gSubKeys[k--]);
- x3 = (int)((uint)x3 >> 1) | x3 << 31;
+ x3 ^= t0 + (2 * t1) + _gSubKeys[k--];
+ x2 = (x2 << 1 | (int) ((uint) x2 >> 31)) ^ (t0 + t1 + _gSubKeys[k--]);
+ x3 = (int) ((uint) x3 >> 1) | x3 << 31;
}
Bits32ToBytes(x0 ^ _gSubKeys[INPUT_WHITEN], outputBuffer, outputOffset);
@@ -178,70 +244,6 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91
};
- /**
- * Define the fixed p0/p1 permutations used in keyed S-box lookup.
- * By changing the following constant definitions, the S-boxes will
- * automatically Get changed in the Twofish engine.
- */
- private const int P_00 = 1;
- private const int P_01 = 0;
- private const int P_02 = 0;
- private const int P_03 = P_01 ^ 1;
- private const int P_04 = 1;
-
- private const int P_10 = 0;
- private const int P_11 = 0;
- private const int P_12 = 1;
- private const int P_13 = P_11 ^ 1;
- private const int P_14 = 0;
-
- private const int P_20 = 1;
- private const int P_21 = 1;
- private const int P_22 = 0;
- private const int P_23 = P_21 ^ 1;
- private const int P_24 = 0;
-
- private const int P_30 = 0;
- private const int P_31 = 1;
- private const int P_32 = 1;
- private const int P_33 = P_31 ^ 1;
- private const int P_34 = 1;
-
- /* Primitive polynomial for GF(256) */
- private const int GF256_FDBK = 0x169;
- private const int GF256_FDBK_2 = GF256_FDBK / 2;
- private const int GF256_FDBK_4 = GF256_FDBK / 4;
-
- private const int RS_GF_FDBK = 0x14D; // field generator
-
- private const int ROUNDS = 16;
- private const int MAX_ROUNDS = 16; // bytes = 128 bits
- private const int MAX_KEY_BITS = 256;
-
- private const int INPUT_WHITEN = 0;
- private const int OUTPUT_WHITEN = INPUT_WHITEN + 16 / 4; // 4
- private const int ROUND_SUBKEYS = OUTPUT_WHITEN + 16 / 4;// 8
-
- private const int TOTAL_SUBKEYS = ROUND_SUBKEYS + 2 * MAX_ROUNDS;// 40
-
- private const int SK_STEP = 0x02020202;
- private const int SK_BUMP = 0x01010101;
- private const int SK_ROTL = 9;
-
- private readonly int[] _gMDS0 = new int[MAX_KEY_BITS];
- private readonly int[] _gMDS1 = new int[MAX_KEY_BITS];
- private readonly int[] _gMDS2 = new int[MAX_KEY_BITS];
- private readonly int[] _gMDS3 = new int[MAX_KEY_BITS];
-
- private readonly int _k64Cnt;
-
- /**
- * _gSubKeys[] and _gSBox[] are eventually used in the
- * encryption and decryption methods.
- */
- private int[] _gSubKeys;
- private int[] _gSBox;
-
private void SetKey(byte[] key)
{
var k32e = new int[MAX_KEY_BITS / 64]; // 4
@@ -285,7 +287,7 @@ private void SetKey(byte[] key)
a += b;
_gSubKeys[i * 2] = a;
a += b;
- _gSubKeys[i * 2 + 1] = a << SK_ROTL | (int)((uint)a >> (32 - SK_ROTL));
+ _gSubKeys[(i * 2) + 1] = a << SK_ROTL | (int)((uint)a >> (32 - SK_ROTL));
}
/*
@@ -305,28 +307,28 @@ private void SetKey(byte[] key)
switch (_k64Cnt & 3)
{
case 1:
- _gSBox[i * 2] = _gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)];
- _gSBox[i * 2 + 1] = _gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)];
- _gSBox[i * 2 + 0x200] = _gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)];
- _gSBox[i * 2 + 0x201] = _gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)];
+ _gSBox[i * 2] = _gMDS0[(P[(P_01 * 256) + b0] & 0xff) ^ M_b0(k0)];
+ _gSBox[(i * 2) + 1] = _gMDS1[(P[(P_11 * 256) + b1] & 0xff) ^ M_b1(k0)];
+ _gSBox[(i * 2) + 0x200] = _gMDS2[(P[(P_21 * 256) + b2] & 0xff) ^ M_b2(k0)];
+ _gSBox[(i * 2) + 0x201] = _gMDS3[(P[(P_31 * 256) + b3] & 0xff) ^ M_b3(k0)];
break;
case 0: /* 256 bits of key */
- b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3);
- b1 = (P[P_14 * 256 + b1] & 0xff) ^ M_b1(k3);
- b2 = (P[P_24 * 256 + b2] & 0xff) ^ M_b2(k3);
- b3 = (P[P_34 * 256 + b3] & 0xff) ^ M_b3(k3);
+ b0 = (P[(P_04 * 256) + b0] & 0xff) ^ M_b0(k3);
+ b1 = (P[(P_14 * 256) + b1] & 0xff) ^ M_b1(k3);
+ b2 = (P[(P_24 * 256) + b2] & 0xff) ^ M_b2(k3);
+ b3 = (P[(P_34 * 256) + b3] & 0xff) ^ M_b3(k3);
goto case 3;
case 3:
- b0 = (P[P_03 * 256 + b0] & 0xff) ^ M_b0(k2);
- b1 = (P[P_13 * 256 + b1] & 0xff) ^ M_b1(k2);
- b2 = (P[P_23 * 256 + b2] & 0xff) ^ M_b2(k2);
- b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2);
+ b0 = (P[(P_03 * 256) + b0] & 0xff) ^ M_b0(k2);
+ b1 = (P[(P_13 * 256) + b1] & 0xff) ^ M_b1(k2);
+ b2 = (P[(P_23 * 256) + b2] & 0xff) ^ M_b2(k2);
+ b3 = (P[(P_33 * 256) + b3] & 0xff) ^ M_b3(k2);
goto case 2;
case 2:
- _gSBox[i * 2] = _gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)];
- _gSBox[i * 2 + 1] = _gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)];
- _gSBox[i * 2 + 0x200] = _gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)];
- _gSBox[i * 2 + 0x201] = _gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)];
+ _gSBox[i * 2] = _gMDS0[(P[(P_01 * 256) + (P[(P_02 * 256) + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)];
+ _gSBox[(i * 2) + 1] = _gMDS1[(P[(P_11 * 256) + (P[(P_12 * 256) + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)];
+ _gSBox[(i * 2) + 0x200] = _gMDS2[(P[(P_21 * 256) + (P[(P_22 * 256) + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)];
+ _gSBox[(i * 2) + 0x201] = _gMDS3[(P[(P_31 * 256) + (P[(P_32 * 256) + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)];
break;
}
#pragma warning restore IDE0010 // Add missing cases
@@ -360,29 +362,29 @@ private int F32(int x, int[] k32)
switch (_k64Cnt & 3)
{
case 1:
- result = _gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)] ^
- _gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)] ^
- _gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)] ^
- _gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)];
+ result = _gMDS0[(P[(P_01 * 256) + b0] & 0xff) ^ M_b0(k0)] ^
+ _gMDS1[(P[(P_11 * 256) + b1] & 0xff) ^ M_b1(k0)] ^
+ _gMDS2[(P[(P_21 * 256) + b2] & 0xff) ^ M_b2(k0)] ^
+ _gMDS3[(P[(P_31 * 256) + b3] & 0xff) ^ M_b3(k0)];
break;
case 0: /* 256 bits of key */
- b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3);
- b1 = (P[P_14 * 256 + b1] & 0xff) ^ M_b1(k3);
- b2 = (P[P_24 * 256 + b2] & 0xff) ^ M_b2(k3);
- b3 = (P[P_34 * 256 + b3] & 0xff) ^ M_b3(k3);
+ b0 = (P[(P_04 * 256) + b0] & 0xff) ^ M_b0(k3);
+ b1 = (P[(P_14 * 256) + b1] & 0xff) ^ M_b1(k3);
+ b2 = (P[(P_24 * 256) + b2] & 0xff) ^ M_b2(k3);
+ b3 = (P[(P_34 * 256) + b3] & 0xff) ^ M_b3(k3);
goto case 3;
case 3:
- b0 = (P[P_03 * 256 + b0] & 0xff) ^ M_b0(k2);
- b1 = (P[P_13 * 256 + b1] & 0xff) ^ M_b1(k2);
- b2 = (P[P_23 * 256 + b2] & 0xff) ^ M_b2(k2);
- b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2);
+ b0 = (P[(P_03 * 256) + b0] & 0xff) ^ M_b0(k2);
+ b1 = (P[(P_13 * 256) + b1] & 0xff) ^ M_b1(k2);
+ b2 = (P[(P_23 * 256) + b2] & 0xff) ^ M_b2(k2);
+ b3 = (P[(P_33 * 256) + b3] & 0xff) ^ M_b3(k2);
goto case 2;
case 2:
result =
- _gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)] ^
- _gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)] ^
- _gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)] ^
- _gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)];
+ _gMDS0[(P[(P_01 * 256) + (P[(P_02 * 256) + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)] ^
+ _gMDS1[(P[(P_11 * 256) + (P[(P_12 * 256) + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)] ^
+ _gMDS2[(P[(P_21 * 256) + (P[(P_22 * 256) + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)] ^
+ _gMDS3[(P[(P_31 * 256) + (P[(P_32 * 256) + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)];
break;
}
#pragma warning restore IDE0010 // Add missing cases
@@ -423,14 +425,14 @@ private static int RS_MDS_Encode(int k0, int k1)
*
* G(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
*
- * where a = primitive root of field generator 0x14D
+ * where a = primitive root of field generator 0x14D.
*
*/
private static int RS_rem(int x)
{
- var b = (int)(((uint)x >> 24) & 0xff);
+ var b = (int) (((uint) x >> 24) & 0xff);
var g2 = ((b << 1) ^ ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff;
- var g3 = ((int)((uint)b >> 1) ^ ((b & 0x01) != 0 ? (int)((uint)RS_GF_FDBK >> 1) : 0)) ^ g2;
+ var g3 = ((int) ((uint) b >> 1) ^ ((b & 0x01) != 0 ? (int) ((uint) RS_GF_FDBK >> 1) : 0)) ^ g2;
return (x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b;
}
@@ -484,18 +486,18 @@ private static int M_b3(int x)
private static int Fe32_0(int[] gSBox1, int x)
{
- return gSBox1[0x000 + 2 * (x & 0xff)] ^
- gSBox1[0x001 + 2 * ((int)((uint)x >> 8) & 0xff)] ^
- gSBox1[0x200 + 2 * ((int)((uint)x >> 16) & 0xff)] ^
- gSBox1[0x201 + 2 * ((int)((uint)x >> 24) & 0xff)];
+ return gSBox1[0x000 + (2 * (x & 0xff))] ^
+ gSBox1[0x001 + (2 * ((int)((uint)x >> 8) & 0xff))] ^
+ gSBox1[0x200 + (2 * ((int)((uint)x >> 16) & 0xff))] ^
+ gSBox1[0x201 + (2 * ((int)((uint)x >> 24) & 0xff))];
}
private static int Fe32_3(int[] gSBox1, int x)
{
- return gSBox1[0x000 + 2 * ((int)((uint)x >> 24) & 0xff)] ^
- gSBox1[0x001 + 2 * (x & 0xff)] ^
- gSBox1[0x200 + 2 * ((int)((uint)x >> 8) & 0xff)] ^
- gSBox1[0x201 + 2 * ((int)((uint)x >> 16) & 0xff)];
+ return gSBox1[0x000 + (2 * ((int) ((uint) x >> 24) & 0xff))] ^
+ gSBox1[0x001 + (2 * (x & 0xff))] ^
+ gSBox1[0x200 + (2 * ((int)((uint)x >> 8) & 0xff))] ^
+ gSBox1[0x201 + (2 * ((int)((uint)x >> 16) & 0xff))];
}
private static int BytesTo32Bits(byte[] b, int p)
diff --git a/src/Renci.SshNet/Security/Cryptography/DigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/DigitalSignature.cs
index e675fe325..3e7cee7db 100644
--- a/src/Renci.SshNet/Security/Cryptography/DigitalSignature.cs
+++ b/src/Renci.SshNet/Security/Cryptography/DigitalSignature.cs
@@ -1,7 +1,7 @@
namespace Renci.SshNet.Security.Cryptography
{
///
- /// Base class for signature implementations
+ /// Base class for signature implementations.
///
public abstract class DigitalSignature
{
@@ -10,7 +10,9 @@ public abstract class DigitalSignature
///
/// The input.
/// The signature.
- /// if signature was successfully verified; otherwise .
+ ///
+ /// if signature was successfully verified; otherwise .
+ ///
public abstract bool Verify(byte[] input, byte[] signature);
///
diff --git a/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs
index 03b4d0169..a5aae17c0 100644
--- a/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs
+++ b/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs
@@ -80,10 +80,10 @@ public override bool Verify(byte[] input, byte[] signature)
var w = BigInteger.ModInverse(s, _key.Q);
// Calculate u1 = H(m)·w mod q
- var u1 = hm * w % _key.Q;
+ var u1 = (hm * w) % _key.Q;
// Calculate u2 = r * w mod q
- var u2 = r * w % _key.Q;
+ var u2 = (r * w) % _key.Q;
u1 = BigInteger.ModPow(_key.G, u1, _key.P);
u2 = BigInteger.ModPow(_key.Y, u2, _key.P);
diff --git a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs
index cdd0c4def..65c6fceec 100644
--- a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs
+++ b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs
@@ -160,7 +160,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs
index 1e8e21bce..2d3cdb956 100644
--- a/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs
+++ b/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs
@@ -64,7 +64,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
index 48c3a74b4..bb0037945 100644
--- a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
+++ b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
@@ -11,17 +11,21 @@ namespace Renci.SshNet.Security
///
public class ED25519Key : Key, IDisposable
{
- private ED25519DigitalSignature _digitalSignature;
-
- private byte[] _publicKey = new byte[Ed25519.PublicKeySizeInBytes];
#pragma warning disable IDE1006 // Naming Styles
+#pragma warning disable SX1309 // Field names should begin with underscore
private readonly byte[] privateKey = new byte[Ed25519.ExpandedPrivateKeySizeInBytes];
+#pragma warning restore SX1309 // Field names should begin with underscore
#pragma warning restore IDE1006 // Naming Styles
+ private ED25519DigitalSignature _digitalSignature;
+ private byte[] _publicKey = new byte[Ed25519.PublicKeySizeInBytes];
private bool _isDisposed;
///
- /// Gets the Key String.
+ /// Gets the name of the key.
///
+ ///
+ /// The name of the key.
+ ///
public override string ToString()
{
return "ssh-ed25519";
@@ -132,7 +136,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs
index 5c9025d54..74f114341 100644
--- a/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs
+++ b/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs
@@ -83,7 +83,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
@@ -106,81 +106,81 @@ protected virtual void Dispose(bool disposing)
{
Dispose(disposing: false);
}
- }
- internal sealed class SshDataSignature : SshData
- {
- private readonly int _signature_size;
+ private sealed class SshDataSignature : SshData
+ {
+ private readonly int _signature_size;
- private byte[] _signature_r;
- private byte[] _signature_s;
+ private byte[] _signature_r;
+ private byte[] _signature_s;
- public byte[] Signature
- {
- get
+ public byte[] Signature
{
- var signature = new byte[_signature_size];
- Buffer.BlockCopy(_signature_r, 0, signature, 0, _signature_r.Length);
- Buffer.BlockCopy(_signature_s, 0, signature, _signature_r.Length, _signature_s.Length);
- return signature;
+ get
+ {
+ var signature = new byte[_signature_size];
+ Buffer.BlockCopy(_signature_r, 0, signature, 0, _signature_r.Length);
+ Buffer.BlockCopy(_signature_s, 0, signature, _signature_r.Length, _signature_s.Length);
+ return signature;
+ }
+ set
+ {
+ var signed_r = new byte[_signature_size / 2];
+ Buffer.BlockCopy(value, 0, signed_r, 0, signed_r.Length);
+ _signature_r = signed_r.ToBigInteger2().ToByteArray().Reverse();
+
+ var signed_s = new byte[_signature_size / 2];
+ Buffer.BlockCopy(value, signed_r.Length, signed_s, 0, signed_s.Length);
+ _signature_s = signed_s.ToBigInteger2().ToByteArray().Reverse();
+ }
}
- set
- {
- var signed_r = new byte[_signature_size / 2];
- Buffer.BlockCopy(value, 0, signed_r, 0, signed_r.Length);
- _signature_r = signed_r.ToBigInteger2().ToByteArray().Reverse();
- var signed_s = new byte[_signature_size / 2];
- Buffer.BlockCopy(value, signed_r.Length, signed_s, 0, signed_s.Length);
- _signature_s = signed_s.ToBigInteger2().ToByteArray().Reverse();
+ public SshDataSignature(int sig_size)
+ {
+ _signature_size = sig_size;
}
- }
- public SshDataSignature(int sig_size)
- {
- _signature_size = sig_size;
- }
+ public SshDataSignature(byte[] data, int sig_size)
+ {
+ _signature_size = sig_size;
+ Load(data);
+ }
- public SshDataSignature(byte[] data, int sig_size)
- {
- _signature_size = sig_size;
- Load(data);
- }
+ protected override void LoadData()
+ {
+ _signature_r = ReadBinary().TrimLeadingZeros().Pad(_signature_size / 2);
+ _signature_s = ReadBinary().TrimLeadingZeros().Pad(_signature_size / 2);
+ }
- protected override void LoadData()
- {
- _signature_r = ReadBinary().TrimLeadingZeros().Pad(_signature_size / 2);
- _signature_s = ReadBinary().TrimLeadingZeros().Pad(_signature_size / 2);
- }
+ protected override void SaveData()
+ {
+ WriteBinaryString(_signature_r.ToBigInteger2().ToByteArray().Reverse());
+ WriteBinaryString(_signature_s.ToBigInteger2().ToByteArray().Reverse());
+ }
- protected override void SaveData()
- {
- WriteBinaryString(_signature_r.ToBigInteger2().ToByteArray().Reverse());
- WriteBinaryString(_signature_s.ToBigInteger2().ToByteArray().Reverse());
- }
+ public new byte[] ReadBinary()
+ {
+ var length = ReadUInt32();
- public new byte[] ReadBinary()
- {
- var length = ReadUInt32();
+ if (length > int.MaxValue)
+ {
+ throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue));
+ }
- if (length > int.MaxValue)
- {
- throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue));
+ return ReadBytes((int) length);
}
- return ReadBytes((int)length);
- }
-
- protected override int BufferCapacity
- {
- get
+ protected override int BufferCapacity
{
- var capacity = base.BufferCapacity;
- capacity += 4; // r length
- capacity += _signature_r.Length; // signature r
- capacity += 4; // s length
- capacity += _signature_s.Length; // signature s
- return capacity;
+ get
+ {
+ var capacity = base.BufferCapacity;
+ capacity += 4; // r length
+ capacity += _signature_r.Length; // signature r
+ capacity += 4; // s length
+ capacity += _signature_s.Length; // signature s
+ return capacity;
+ }
}
}
}
diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs
index fe38dfec2..455bb1363 100644
--- a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs
+++ b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs
@@ -1,12 +1,11 @@
using System;
#if NETFRAMEWORK
+using System.Globalization;
using System.IO;
-#endif // NETFRAMEWORK
-using System.Text;
-#if NETFRAMEWORK
using System.Runtime.InteropServices;
#endif // NETFRAMEWORK
using System.Security.Cryptography;
+using System.Text;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
@@ -18,9 +17,11 @@ namespace Renci.SshNet.Security
///
public class EcdsaKey : Key, IDisposable
{
- internal const string ECDSA_P256_OID_VALUE = "1.2.840.10045.3.1.7"; // Also called nistP256 or secP256r1
- internal const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // Also called nistP384 or secP384r1
- internal const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // Also called nistP521or secP521r1
+#pragma warning disable SA1310 // Field names should not contain underscore
+ private const string ECDSA_P256_OID_VALUE = "1.2.840.10045.3.1.7"; // Also called nistP256 or secP256r1
+ private const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // Also called nistP384 or secP384r1
+ private const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // Also called nistP521or secP521r1
+#pragma warning restore SA1310 // Field names should not contain underscore
private EcdsaDigitalSignature _digitalSignature;
private bool _isDisposed;
@@ -52,7 +53,7 @@ internal enum KeyBlobMagicNumber
internal struct BCRYPT_ECCKEY_BLOB
{
internal KeyBlobMagicNumber Magic;
- internal int cbKey;
+ internal int CbKey;
}
#endif
@@ -69,7 +70,7 @@ public override string ToString()
#if NETFRAMEWORK
///
- /// Gets the HashAlgorithm to use
+ /// Gets the HashAlgorithm to use.
///
public CngAlgorithm HashAlgorithm
{
@@ -84,7 +85,7 @@ public CngAlgorithm HashAlgorithm
case 521:
return CngAlgorithm.Sha512;
default:
- throw new SshException("Unknown KeySize: " + Ecdsa.KeySize);
+ throw new SshException("Unknown KeySize: " + Ecdsa.KeySize.ToString(CultureInfo.InvariantCulture));
}
}
}
@@ -380,6 +381,7 @@ private void Import(string curve_oid, byte[] publickey, byte[] privatekey)
bw.Write(privatekey); // d
}
}
+
_key = CngKey.Import(blob, privatekey is null ? CngKeyBlobFormat.EccPublicBlob : CngKeyBlobFormat.EccPrivateBlob);
Ecdsa = new ECDsaCng(_key);
@@ -413,13 +415,13 @@ private void Import(string curve_oid, byte[] publickey, byte[] privatekey)
private static string GetCurveOid(string curve_s)
{
- switch (curve_s.ToLower())
+ switch (curve_s.ToUpperInvariant())
{
- case "nistp256":
+ case "NISTP256":
return ECDSA_P256_OID_VALUE;
- case "nistp384":
+ case "NISTP384":
return ECDSA_P384_OID_VALUE;
- case "nistp521":
+ case "NISTP521":
return ECDSA_P521_OID_VALUE;
default:
throw new SshException("Unexpected Curve Name: " + curve_s);
diff --git a/src/Renci.SshNet/Security/Cryptography/HMACMD5.cs b/src/Renci.SshNet/Security/Cryptography/HMACMD5.cs
index cd129fe69..bd9585448 100644
--- a/src/Renci.SshNet/Security/Cryptography/HMACMD5.cs
+++ b/src/Renci.SshNet/Security/Cryptography/HMACMD5.cs
@@ -11,22 +11,29 @@ public class HMACMD5 : System.Security.Cryptography.HMACMD5
private readonly int _hashSize;
///
- /// Initializes a with the specified key.
+ /// Initializes a new instance of the class with the specified key.
///
/// The key.
public HMACMD5(byte[] key)
+#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms
: base(key)
+#pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms
{
+#pragma warning disable MA0056 // Do not call overridable members in constructor
_hashSize = base.HashSize;
+#pragma warning restore MA0056 // Do not call overridable members in constructor
}
///
- /// Initializes a with the specified key and size of the computed hash code.
+ /// Initializes a new instance of the class with the specified key
+ /// and size of the computed hash code.
///
/// The key.
/// The size, in bits, of the computed hash code.
public HMACMD5(byte[] key, int hashSize)
+#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms
: base(key)
+#pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms
{
_hashSize = hashSize;
}
@@ -50,7 +57,9 @@ public override int HashSize
///
protected override byte[] HashFinal()
{
+#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms
var hash = base.HashFinal();
+#pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms
return hash.Take(HashSize / 8);
}
}
diff --git a/src/Renci.SshNet/Security/Cryptography/HMACSHA1.cs b/src/Renci.SshNet/Security/Cryptography/HMACSHA1.cs
index 05a9730ed..ca4adfdaf 100644
--- a/src/Renci.SshNet/Security/Cryptography/HMACSHA1.cs
+++ b/src/Renci.SshNet/Security/Cryptography/HMACSHA1.cs
@@ -16,9 +16,13 @@ public class HMACSHA1 : System.Security.Cryptography.HMACSHA1
///
/// The key.
public HMACSHA1(byte[] key)
+#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms
: base(key)
+#pragma warning restore CA5350 // Do Not Use Weak Cryptographic Algorithms
{
+#pragma warning disable MA0056 // Do not call overridable members in constructor
_hashSize = base.HashSize;
+#pragma warning restore MA0056 // Do not call overridable members in constructor
}
///
@@ -27,7 +31,9 @@ public HMACSHA1(byte[] key)
/// The key.
/// The size, in bits, of the computed hash code.
public HMACSHA1(byte[] key, int hashSize)
+#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms
: base(key)
+#pragma warning restore CA5350 // Do Not Use Weak Cryptographic Algorithms
{
_hashSize = hashSize;
}
@@ -51,7 +57,9 @@ public override int HashSize
///
protected override byte[] HashFinal()
{
+#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms
var hash = base.HashFinal();
+#pragma warning restore CA5350 // Do Not Use Weak Cryptographic Algorithms
return hash.Take(HashSize / 8);
}
}
diff --git a/src/Renci.SshNet/Security/Cryptography/HMACSHA256.cs b/src/Renci.SshNet/Security/Cryptography/HMACSHA256.cs
index 2598704e4..20f752a86 100644
--- a/src/Renci.SshNet/Security/Cryptography/HMACSHA256.cs
+++ b/src/Renci.SshNet/Security/Cryptography/HMACSHA256.cs
@@ -11,17 +11,20 @@ public class HMACSHA256 : System.Security.Cryptography.HMACSHA256
private readonly int _hashSize;
///
- /// Initializes a with the specified key.
+ /// Initializes a new instance of the class with the specified key.
///
/// The key.
public HMACSHA256(byte[] key)
: base(key)
{
+#pragma warning disable MA0056 // Do not call overridable members in constructor
_hashSize = base.HashSize;
+#pragma warning restore MA0056 // Do not call overridable members in constructor
}
///
- /// Initializes a with the specified key and size of the computed hash code.
+ /// Initializes a new instance of the class with the specified key
+ /// and size of the computed hash code.
///
/// The key.
/// The size, in bits, of the computed hash code.
@@ -48,7 +51,6 @@ public override int HashSize
///
/// The computed hash code.
///
-
protected override byte[] HashFinal()
{
var hash = base.HashFinal();
diff --git a/src/Renci.SshNet/Security/Cryptography/HMACSHA384.cs b/src/Renci.SshNet/Security/Cryptography/HMACSHA384.cs
index f5f0b26c5..e13d720c8 100644
--- a/src/Renci.SshNet/Security/Cryptography/HMACSHA384.cs
+++ b/src/Renci.SshNet/Security/Cryptography/HMACSHA384.cs
@@ -11,17 +11,20 @@ public class HMACSHA384 : System.Security.Cryptography.HMACSHA384
private readonly int _hashSize;
///
- /// Initializes a with the specified key.
+ /// Initializes a new instance of the class with the specified key.
///
/// The key.
public HMACSHA384(byte[] key)
: base(key)
{
+#pragma warning disable MA0056 // Do not call overridable members in constructor
_hashSize = base.HashSize;
+#pragma warning restore MA0056 // Do not call overridable members in constructor
}
///
- /// Initializes a with the specified key and size of the computed hash code.
+ /// Initializes a new instance of the class with the specified key
+ /// and size of the computed hash code.
///
/// The key.
/// The size, in bits, of the computed hash code.
diff --git a/src/Renci.SshNet/Security/Cryptography/HMACSHA512.cs b/src/Renci.SshNet/Security/Cryptography/HMACSHA512.cs
index 72e758155..8d756efef 100644
--- a/src/Renci.SshNet/Security/Cryptography/HMACSHA512.cs
+++ b/src/Renci.SshNet/Security/Cryptography/HMACSHA512.cs
@@ -11,17 +11,20 @@ public class HMACSHA512 : System.Security.Cryptography.HMACSHA512
private readonly int _hashSize;
///
- /// Initializes a with the specified key.
+ /// Initializes a new instance of the class with the specified key.
///
/// The key.
public HMACSHA512(byte[] key)
: base(key)
{
+#pragma warning disable MA0056 // Do not call overridable members in constructor
_hashSize = base.HashSize;
+#pragma warning restore MA0056 // Do not call overridable members in constructor
}
///
- /// Initializes a with the specified key and size of the computed hash code.
+ /// Initializes a new instance of the class with the specified key
+ /// and size of the computed hash code.
///
/// The key.
/// The size, in bits, of the computed hash code.
diff --git a/src/Renci.SshNet/Security/Cryptography/Key.cs b/src/Renci.SshNet/Security/Cryptography/Key.cs
index 3110a2b91..81580a1ab 100644
--- a/src/Renci.SshNet/Security/Cryptography/Key.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Key.cs
@@ -14,7 +14,9 @@ public abstract class Key
///
/// Specifies array of big integers that represent private key.
///
+#pragma warning disable SA1401 // Fields should be private
protected BigInteger[] _privateKey;
+#pragma warning restore SA1401 // Fields should be private
///
/// Gets the default digital signature implementation for this key.
diff --git a/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs
index ccb252efd..00a4898f9 100644
--- a/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs
+++ b/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs
@@ -18,7 +18,8 @@ public class RsaDigitalSignature : CipherDigitalSignature, IDisposable
/// The RSA key.
public RsaDigitalSignature(RsaKey rsaKey)
: this(rsaKey, HashAlgorithmName.SHA1)
- { }
+ {
+ }
///
/// Initializes a new instance of the class.
@@ -58,7 +59,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs
index 2f1367a29..19c8bedc7 100644
--- a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs
+++ b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs
@@ -5,15 +5,19 @@
namespace Renci.SshNet.Security
{
///
- /// Contains RSA private and public key
+ /// Contains the RSA private and public key.
///
public class RsaKey : Key, IDisposable
{
private bool _isDisposed;
+ private RsaDigitalSignature _digitalSignature;
///
- /// Gets the Key String.
+ /// Gets the name of the key.
///
+ ///
+ /// The name of the key.
+ ///
public override string ToString()
{
return "ssh-rsa";
@@ -22,6 +26,9 @@ public override string ToString()
///
/// Gets the modulus.
///
+ ///
+ /// The modulus.
+ ///
public BigInteger Modulus
{
get
@@ -33,6 +40,9 @@ public BigInteger Modulus
///
/// Gets the exponent.
///
+ ///
+ /// The exponent.
+ ///
public BigInteger Exponent
{
get
@@ -44,6 +54,9 @@ public BigInteger Exponent
///
/// Gets the D.
///
+ ///
+ /// The D.
+ ///
public BigInteger D
{
get
@@ -60,6 +73,9 @@ public BigInteger D
///
/// Gets the P.
///
+ ///
+ /// The P.
+ ///
public BigInteger P
{
get
@@ -69,7 +85,6 @@ public BigInteger P
return _privateKey[3];
}
-
return BigInteger.Zero;
}
}
@@ -77,6 +92,9 @@ public BigInteger P
///
/// Gets the Q.
///
+ ///
+ /// The Q.
+ ///
public BigInteger Q
{
get
@@ -93,6 +111,9 @@ public BigInteger Q
///
/// Gets the DP.
///
+ ///
+ /// The DP.
+ ///
public BigInteger DP
{
get
@@ -109,6 +130,9 @@ public BigInteger DP
///
/// Gets the DQ.
///
+ ///
+ /// The DQ.
+ ///
public BigInteger DQ
{
get
@@ -125,6 +149,9 @@ public BigInteger DQ
///
/// Gets the inverse Q.
///
+ ///
+ /// The inverse Q.
+ ///
public BigInteger InverseQ
{
get
@@ -152,10 +179,8 @@ public override int KeyLength
}
}
- private RsaDigitalSignature _digitalSignature;
-
///
- ///
+ /// Gets the digital signature implementation for this key.
///
///
/// An implementation of an RSA digital signature using the SHA-1 hash algorithm.
@@ -253,7 +278,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/Security/GroupExchangeHashData.cs b/src/Renci.SshNet/Security/GroupExchangeHashData.cs
index 82b889bb8..921841094 100644
--- a/src/Renci.SshNet/Security/GroupExchangeHashData.cs
+++ b/src/Renci.SshNet/Security/GroupExchangeHashData.cs
@@ -16,7 +16,6 @@ public string ServerVersion
set { _serverVersion = Utf8.GetBytes(value); }
}
-
public string ClientVersion
{
private get { return Utf8.GetString(_clientVersion, 0, _clientVersion.Length); }
diff --git a/src/Renci.SshNet/Security/KeyExchange.cs b/src/Renci.SshNet/Security/KeyExchange.cs
index 6d8c53748..5024941bf 100644
--- a/src/Renci.SshNet/Security/KeyExchange.cs
+++ b/src/Renci.SshNet/Security/KeyExchange.cs
@@ -13,7 +13,7 @@
namespace Renci.SshNet.Security
{
///
- /// Represents base class for different key exchange algorithm implementations
+ /// Represents base class for different key exchange algorithm implementations.
///
public abstract class KeyExchange : Algorithm, IKeyExchange
{
@@ -511,7 +511,7 @@ public void Dispose()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs
index 5cdba5b4d..4f31514a7 100644
--- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs
+++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs
@@ -6,10 +6,11 @@
namespace Renci.SshNet.Security
{
///
- /// Represents base class for Diffie Hellman key exchange algorithm
+ /// Represents base class for Diffie Hellman key exchange algorithm.
///
internal abstract class KeyExchangeDiffieHellman : KeyExchange
{
+#pragma warning disable SA1401 // Fields should be private
///
/// Specifies key exchange group number.
///
@@ -26,7 +27,7 @@ internal abstract class KeyExchangeDiffieHellman : KeyExchange
protected byte[] _clientPayload;
///
- /// Specifies server payload
+ /// Specifies server payload.
///
protected byte[] _serverPayload;
@@ -54,6 +55,7 @@ internal abstract class KeyExchangeDiffieHellman : KeyExchange
/// Specifies signature data.
///
protected byte[] _signature;
+#pragma warning restore SA1401 // Fields should be private
///
/// Gets the size, in bits, of the computed hash code.
diff --git a/src/Renci.SshNet/Security/KeyExchangeEC.cs b/src/Renci.SshNet/Security/KeyExchangeEC.cs
index f2ae22fb7..4368affbf 100644
--- a/src/Renci.SshNet/Security/KeyExchangeEC.cs
+++ b/src/Renci.SshNet/Security/KeyExchangeEC.cs
@@ -4,13 +4,14 @@ namespace Renci.SshNet.Security
{
internal abstract class KeyExchangeEC : KeyExchange
{
+#pragma warning disable SA1401 // Fields should be private
///
- /// Specifies client payload
+ /// Specifies client payload.
///
protected byte[] _clientPayload;
///
- /// Specifies server payload
+ /// Specifies server payload.
///
protected byte[] _serverPayload;
@@ -33,6 +34,7 @@ internal abstract class KeyExchangeEC : KeyExchange
/// Specifies signature data.
///
protected byte[] _signature;
+#pragma warning restore SA1401 // Fields should be private
///
/// Gets the size, in bits, of the computed hash code.
diff --git a/src/Renci.SshNet/Security/KeyExchangeHash.cs b/src/Renci.SshNet/Security/KeyExchangeHashData.cs
similarity index 100%
rename from src/Renci.SshNet/Security/KeyExchangeHash.cs
rename to src/Renci.SshNet/Security/KeyExchangeHashData.cs
diff --git a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs
index f0d2b41d8..0a0232a58 100644
--- a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs
+++ b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs
@@ -13,18 +13,27 @@ namespace Renci.SshNet.Security
public class KeyHostAlgorithm : HostAlgorithm
{
///
- /// The key used in this host key algorithm.
+ /// Gets the key used in this host key algorithm.
///
+ ///
+ /// The key used in this host key algorithm.
+ ///
public Key Key { get; private set; }
///
- /// The signature implementation used in this host key algorithm.
+ /// Gets the signature implementation used in this host key algorithm.
///
+ ///
+ /// The signature implementation used in this host key algorithm.
+ ///
public DigitalSignature DigitalSignature { get; private set; }
///
/// Gets the encoded public key data.
///
+ ///
+ /// The encoded public key data.
+ ///
public override byte[] Data
{
get
@@ -202,11 +211,13 @@ protected override int BufferCapacity
var capacity = base.BufferCapacity;
capacity += 4; // Name length
capacity += _name.Length; // Name
+
foreach (var key in _keys)
{
capacity += 4; // Key length
capacity += key.Length; // Key
}
+
return capacity;
}
}
@@ -246,8 +257,11 @@ protected override void SaveData()
internal sealed class SignatureKeyData : SshData
{
///
- /// Gets or sets the signature format identifier
+ /// Gets or sets the signature format identifier.
///
+ ///
+ /// The signature format identifier.
+ ///
public string AlgorithmName { get; set; }
///
diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs
index 23eef6f78..2d913d072 100644
--- a/src/Renci.SshNet/Session.cs
+++ b/src/Renci.SshNet/Session.cs
@@ -121,6 +121,12 @@ public class Session : ISession
///
private readonly object _socketDisposeLock = new object();
+ ///
+ /// Holds an object that is used to ensure only a single thread can connect
+ /// and lazy initialize the at any given time.
+ ///
+ private readonly object _connectAndLazySemaphoreInitLock = new object();
+
///
/// Holds metadata about session messages.
///
@@ -192,6 +198,10 @@ public class Session : ISession
private SemaphoreLight _sessionSemaphore;
+ private bool _isDisconnectMessageSent;
+
+ private uint _nextChannelNumber;
+
///
/// Holds connection socket.
///
@@ -209,7 +219,7 @@ public SemaphoreLight SessionSemaphore
{
if (_sessionSemaphore is null)
{
- lock (this)
+ lock (_connectAndLazySemaphoreInitLock)
{
_sessionSemaphore ??= new SemaphoreLight(ConnectionInfo.MaxSessions);
}
@@ -219,10 +229,6 @@ public SemaphoreLight SessionSemaphore
}
}
- private bool _isDisconnectMessageSent;
-
- private uint _nextChannelNumber;
-
///
/// Gets the next channel number.
///
@@ -235,7 +241,7 @@ private uint NextChannelNumber
{
uint result;
- lock (this)
+ lock (_connectAndLazySemaphoreInitLock)
{
result = _nextChannelNumber++;
}
@@ -586,7 +592,7 @@ public void Connect()
return;
}
- lock (this)
+ lock (_connectAndLazySemaphoreInitLock)
{
// If connected don't connect again
if (IsConnected)
@@ -635,7 +641,7 @@ public void Connect()
// Start incoming request listener
// ToDo: Make message pump async, to not consume a thread for every session
- ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
+ _ = ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
// Wait for key exchange to be completed
WaitOnHandle(_keyExchangeCompletedWaitHandle);
@@ -747,7 +753,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
// Start incoming request listener
// ToDo: Make message pump async, to not consume a thread for every session
- ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
+ _ = ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
// Wait for key exchange to be completed
WaitOnHandle(_keyExchangeCompletedWaitHandle);
@@ -1257,8 +1263,8 @@ private Message ReceiveMessage(Socket socket)
var clientHash = _serverMac.ComputeHash(data, 0, data.Length - serverMacLength);
var serverHash = data.Take(data.Length - serverMacLength, serverMacLength);
- // TODO add IsEqualTo overload that takes left+right index and number of bytes to compare;
- // TODO that way we can eliminate the extra allocation of the Take above
+ // TODO Add IsEqualTo overload that takes left+right index and number of bytes to compare.
+ // TODO That way we can eliminate the extra allocation of the Take above.
if (!serverHash.IsEqualTo(clientHash))
{
throw new SshConnectionException("MAC error", DisconnectReason.MacError);
@@ -1269,10 +1275,10 @@ private Message ReceiveMessage(Socket socket)
{
data = _serverDecompression.Decompress(data, messagePayloadOffset, messagePayloadLength);
- // data now only contains the decompressed payload, and as such the offset is reset to zero
+ // Data now only contains the decompressed payload, and as such the offset is reset to zero
messagePayloadOffset = 0;
- // the length of the payload is now the complete decompressed content
+ // The length of the payload is now the complete decompressed content
messagePayloadLength = data.Length;
}
@@ -1285,10 +1291,10 @@ private void TrySendDisconnect(DisconnectReason reasonCode, string message)
{
var disconnectMessage = new DisconnectMessage(reasonCode, message);
- // send the disconnect message, but ignore the outcome
+ // Send the disconnect message, but ignore the outcome
_ = TrySendMessage(disconnectMessage);
- // mark disconnect message sent regardless of whether the send sctually succeeded
+ // Mark disconnect message sent regardless of whether the send sctually succeeded
_isDisconnectMessageSent = true;
}
@@ -1440,12 +1446,9 @@ internal void OnNewKeysReceived(NewKeysMessage message)
_serverDecompression = _keyExchange.CreateDecompressor();
// Dispose of old KeyExchange object as it is no longer needed.
- if (_keyExchange != null)
- {
- _keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
- _keyExchange.Dispose();
- _keyExchange = null;
- }
+ _keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
+ _keyExchange.Dispose();
+ _keyExchange = null;
// Enable activated messages that are not key exchange related
_sshMessageFactory.EnableActivatedMessages();
@@ -2059,6 +2062,13 @@ IConnectionInfo ISession.ConnectionInfo
get { return ConnectionInfo; }
}
+ ///
+ /// Gets a that can be used to wait for the message listener loop to complete.
+ ///
+ ///
+ /// A that can be used to wait for the message listener loop to complete, or
+ /// when the session has not been connected.
+ ///
WaitHandle ISession.MessageListenerCompleted
{
get { return _messageListenerCompleted; }
@@ -2089,6 +2099,9 @@ IChannelDirectTcpip ISession.CreateChannelDirectTcpip()
///
/// Creates a "forwarded-tcpip" SSH channel.
///
+ /// The number of the remote channel.
+ /// The window size of the remote channel.
+ /// The data packet size of the remote channel.
///
/// A new "forwarded-tcpip" SSH channel.
///
diff --git a/src/Renci.SshNet/Sftp/Flags.cs b/src/Renci.SshNet/Sftp/Flags.cs
index 68ae5b10b..6f0a0e6ca 100644
--- a/src/Renci.SshNet/Sftp/Flags.cs
+++ b/src/Renci.SshNet/Sftp/Flags.cs
@@ -3,31 +3,44 @@
namespace Renci.SshNet.Sftp
{
[Flags]
+#pragma warning disable S2344 // Enumeration type names should not have "Flags" or "Enum" suffixes
+#pragma warning disable MA0062 // Non-flags enums should not be marked with "FlagsAttribute"
internal enum Flags
+#pragma warning restore MA0062 // Non-flags enums should not be marked with "FlagsAttribute"
+#pragma warning restore S2344 // Enumeration type names should not have "Flags" or "Enum" suffixes
{
+ ///
+ /// None.
+ ///
None = 0x00000000,
+
///
- /// SSH_FXF_READ
+ /// SSH_FXF_READ.
///
Read = 0x00000001,
+
///
- /// SSH_FXF_WRITE
+ /// SSH_FXF_WRITE.
///
Write = 0x00000002,
+
///
- /// SSH_FXF_APPEND
+ /// SSH_FXF_APPEND.
///
Append = 0x00000004,
+
///
- /// SSH_FXF_CREAT
+ /// SSH_FXF_CREAT.
///
CreateNewOrOpen = 0x00000008,
+
///
- /// SSH_FXF_TRUNC
+ /// SSH_FXF_TRUNC.
///
Truncate = 0x00000010,
+
///
- /// SSH_FXF_EXCL
+ /// SSH_FXF_EXCL.
///
CreateNew = 0x00000028
}
diff --git a/src/Renci.SshNet/Sftp/ISftpFile.cs b/src/Renci.SshNet/Sftp/ISftpFile.cs
index a74e8fbf9..02dff7215 100644
--- a/src/Renci.SshNet/Sftp/ISftpFile.cs
+++ b/src/Renci.SshNet/Sftp/ISftpFile.cs
@@ -156,7 +156,7 @@ public interface ISftpFile
/// Gets or sets a value indicating whether the owner can write into this file.
///
///
- /// if owner can write into this file; otherwise, .
+ /// if owner can write into this file; otherwise, .
///
bool OwnerCanWrite { get; set; }
@@ -164,7 +164,7 @@ public interface ISftpFile
/// Gets or sets a value indicating whether the owner can execute this file.
///
///
- /// if owner can execute this file; otherwise, .
+ /// if owner can execute this file; otherwise, .
///
bool OwnerCanExecute { get; set; }
@@ -172,7 +172,7 @@ public interface ISftpFile
/// Gets or sets a value indicating whether the group members can read from this file.
///
///
- /// if group members can read from this file; otherwise, .
+ /// if group members can read from this file; otherwise, .
///
bool GroupCanRead { get; set; }
@@ -180,7 +180,7 @@ public interface ISftpFile
/// Gets or sets a value indicating whether the group members can write into this file.
///
///
- /// if group members can write into this file; otherwise, .
+ /// if group members can write into this file; otherwise, .
///
bool GroupCanWrite { get; set; }
@@ -188,7 +188,7 @@ public interface ISftpFile
/// Gets or sets a value indicating whether the group members can execute this file.
///
///
- /// if group members can execute this file; otherwise, .
+ /// if group members can execute this file; otherwise, .
///
bool GroupCanExecute { get; set; }
@@ -196,7 +196,7 @@ public interface ISftpFile
/// Gets or sets a value indicating whether the others can read from this file.
///
///
- /// if others can read from this file; otherwise, .
+ /// if others can read from this file; otherwise, .
///
bool OthersCanRead { get; set; }
@@ -204,7 +204,7 @@ public interface ISftpFile
/// Gets or sets a value indicating whether the others can write into this file.
///
///
- /// if others can write into this file; otherwise, .
+ /// if others can write into this file; otherwise, .
///
bool OthersCanWrite { get; set; }
@@ -212,7 +212,7 @@ public interface ISftpFile
/// Gets or sets a value indicating whether the others can execute this file.
///
///
- /// if others can execute this file; otherwise, .
+ /// if others can execute this file; otherwise, .
///
bool OthersCanExecute { get; set; }
diff --git a/src/Renci.SshNet/Sftp/ISftpFileReader.cs b/src/Renci.SshNet/Sftp/ISftpFileReader.cs
index 684cc4187..823b2e23a 100644
--- a/src/Renci.SshNet/Sftp/ISftpFileReader.cs
+++ b/src/Renci.SshNet/Sftp/ISftpFileReader.cs
@@ -1,9 +1,23 @@
using System;
+using Renci.SshNet.Common;
+
namespace Renci.SshNet.Sftp
{
+ ///
+ /// Reads a given file.
+ ///
internal interface ISftpFileReader : IDisposable
{
+ ///
+ /// Reads a sequence of bytes from the current file and advances the position within the file by the number of bytes read.
+ ///
+ ///
+ /// The sequence of bytes read from the file, or a zero-length array if the end of the file
+ /// has been reached.
+ ///
+ /// The current is disposed.
+ /// Attempting to read beyond the end of the file.
byte[] Read();
}
}
diff --git a/src/Renci.SshNet/Sftp/ISftpResponseFactory.cs b/src/Renci.SshNet/Sftp/ISftpResponseFactory.cs
index afb440c10..ae57110fc 100644
--- a/src/Renci.SshNet/Sftp/ISftpResponseFactory.cs
+++ b/src/Renci.SshNet/Sftp/ISftpResponseFactory.cs
@@ -2,8 +2,21 @@
namespace Renci.SshNet.Sftp
{
+ ///
+ /// Represents a factory for creating SFTP response messages.
+ ///
internal interface ISftpResponseFactory
{
+ ///
+ /// Creates a SFTP response message for the specified protocol version and message type, and
+ /// with the specified .
+ ///
+ /// The protocol version.
+ /// The message type.
+ /// The .
+ ///
+ /// A .
+ ///
SftpMessage Create(uint protocolVersion, byte messageType, Encoding encoding);
}
}
diff --git a/src/Renci.SshNet/Sftp/ISftpSession.cs b/src/Renci.SshNet/Sftp/ISftpSession.cs
index 31b091fac..ed9b72369 100644
--- a/src/Renci.SshNet/Sftp/ISftpSession.cs
+++ b/src/Renci.SshNet/Sftp/ISftpSession.cs
@@ -7,6 +7,9 @@
namespace Renci.SshNet.Sftp
{
+ ///
+ /// Represents an SFTP session.
+ ///
internal interface ISftpSession : ISubsystemSession
{
///
@@ -40,25 +43,44 @@ internal interface ISftpSession : ISubsystemSession
///
string GetCanonicalPath(string path);
+ ///
+ /// Asynchronously resolves a given path into an absolute path on the server.
+ ///
+ /// The path to resolve.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents an asynchronous operation to resolve into
+ /// an absolute path. The value of its contains the absolute
+ /// path of the specified path.
+ ///
Task GetCanonicalPathAsync(string path, CancellationToken cancellationToken);
///
- /// Performs SSH_FXP_FSTAT request.
+ /// Asynchronously performs a SSH_FXP_FSTAT request.
///
/// The handle.
- /// if set to returns instead of throwing an exception.
+ /// If set to , is returned in case of an error.
///
- /// File attributes
+ /// The file attributes.
///
SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError);
+ ///
+ /// Asynchronously performs a SSH_FXP_FSTAT request.
+ ///
+ /// The handle.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task the represents the asynchronous SSH_FXP_FSTAT request. The value of its
+ /// contains the file attributes of the specified handle.
+ ///
Task RequestFStatAsync(byte[] handle, CancellationToken cancellationToken);
///
/// Performs SSH_FXP_STAT request.
///
/// The path.
- /// if set to returns null instead of throwing an exception.
+ /// If set to , is returned in case of an error.
///
/// File attributes.
///
@@ -90,7 +112,7 @@ internal interface ISftpSession : ISubsystemSession
///
/// The path.
///
- /// File attributes
+ /// File attributes.
///
SftpFileAttributes RequestLStat(string path);
@@ -122,14 +144,26 @@ internal interface ISftpSession : ISubsystemSession
void RequestMkDir(string path);
///
- /// Performs SSH_FXP_OPEN request.
+ /// Performs a SSH_FXP_OPEN request.
///
/// The path.
/// The flags.
- /// if set to returns instead of throwing an exception.
- /// File handle.
+ /// If set to , is returned in case of an error.
+ ///
+ /// The file handle for the specified path.
+ ///
byte[] RequestOpen(string path, Flags flags, bool nullOnError = false);
+ ///
+ /// Asynchronously performs a SSH_FXP_OPEN request.
+ ///
+ /// The path.
+ /// The flags.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task the represents the asynchronous SSH_FXP_OPEN request. The value of its
+ /// contains the file handle of the specified path.
+ ///
Task RequestOpenAsync(string path, Flags flags, CancellationToken cancellationToken);
///
@@ -159,13 +193,24 @@ internal interface ISftpSession : ISubsystemSession
byte[] EndOpen(SftpOpenAsyncResult asyncResult);
///
- /// Performs SSH_FXP_OPENDIR request.
+ /// Performs a SSH_FXP_OPENDIR request.
///
/// The path.
- /// if set to returns null instead of throwing an exception.
- /// File handle.
+ /// If set to , is returned in case of an error.
+ ///
+ /// A file handle for the specified path.
+ ///
byte[] RequestOpenDir(string path, bool nullOnError = false);
+ ///
+ /// Asynchronously performs a SSH_FXP_OPENDIR request.
+ ///
+ /// The path.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_OPENDIR request. The value of its
+ /// contains the handle of the specified path.
+ ///
Task RequestOpenDirAsync(string path, CancellationToken cancellationToken);
///
@@ -181,7 +226,7 @@ internal interface ISftpSession : ISubsystemSession
/// The handle.
/// The offset.
/// The length.
- /// data array; null if EOF
+ /// data array; null if EOF.
byte[] RequestRead(byte[] handle, ulong offset, uint length);
///
@@ -211,15 +256,41 @@ internal interface ISftpSession : ISubsystemSession
/// is .
byte[] EndRead(SftpReadAsyncResult asyncResult);
+ ///
+ /// Asynchronously performs a SSH_FXP_READ request.
+ ///
+ /// The handle to the file to read from.
+ /// The offset in the file to start reading from.
+ /// The number of bytes to read.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_READ request. The value of
+ /// its contains the data read from the file, or an empty
+ /// array when the end of the file is reached.
+ ///
Task RequestReadAsync(byte[] handle, ulong offset, uint length, CancellationToken cancellationToken);
///
- /// Performs SSH_FXP_READDIR request.
+ /// Performs a SSH_FXP_READDIR request.
///
- /// The handle.
- ///
+ /// The handle of the directory to read.
+ ///
+ /// A where the key is the name of a file in the directory
+ /// and the value is the of the file.
+ ///
KeyValuePair[] RequestReadDir(byte[] handle);
+ ///
+ /// Performs a SSH_FXP_READDIR request.
+ ///
+ /// The handle of the directory to read.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_READDIR request. The value of its
+ /// contains a where the
+ /// key is the name of a file in the directory and the value is the
+ /// of the file.
+ ///
Task[]> RequestReadDirAsync(byte[] handle, CancellationToken cancellationToken);
///
@@ -244,20 +315,37 @@ internal interface ISftpSession : ISubsystemSession
string EndRealPath(SftpRealPathAsyncResult asyncResult);
///
- /// Performs SSH_FXP_REMOVE request.
+ /// Performs a SSH_FXP_REMOVE request.
///
/// The path.
void RequestRemove(string path);
+ ///
+ /// Asynchronously performs a SSH_FXP_REMOVE request.
+ ///
+ /// The path.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_REMOVE request.
+ ///
Task RequestRemoveAsync(string path, CancellationToken cancellationToken);
///
- /// Performs SSH_FXP_RENAME request.
+ /// Performs a SSH_FXP_RENAME request.
///
/// The old path.
/// The new path.
void RequestRename(string oldPath, string newPath);
+ ///
+ /// Asynchronously performs a SSH_FXP_RENAME request.
+ ///
+ /// The old path.
+ /// The new path.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_RENAME request.
+ ///
Task RequestRenameAsync(string oldPath, string newPath, CancellationToken cancellationToken);
///
@@ -274,13 +362,26 @@ internal interface ISftpSession : ISubsystemSession
void RequestSetStat(string path, SftpFileAttributes attributes);
///
- /// Performs statvfs@openssh.com extended request.
+ /// Performs a statvfs@openssh.com extended request.
///
/// The path.
- /// if set to [null on error].
- ///
+ /// If set to , is returned in case of an error.
+ ///
+ /// The file system information for the specified path, or when
+ /// the request failed and is .
+ ///
SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false);
+ ///
+ /// Asynchronously performs a statvfs@openssh.com extended request.
+ ///
+ /// The path.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the statvfs@openssh.com extended request. The value of its
+ /// contains the file system information for the specified
+ /// path.
+ ///
Task RequestStatVfsAsync(string path, CancellationToken cancellationToken);
///
@@ -315,14 +416,34 @@ void RequestWrite(byte[] handle,
AutoResetEvent wait,
Action writeCompleted = null);
+ ///
+ /// Asynchronouly performs a SSH_FXP_WRITE request.
+ ///
+ /// The handle.
+ /// The the zero-based offset (in bytes) relative to the beginning of the file that the write must start at.
+ /// The buffer holding the data to write.
+ /// the zero-based offset in at which to begin taking bytes to write.
+ /// The length (in bytes) of the data to write.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_WRITE request.
+ ///
Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, CancellationToken cancellationToken);
///
- /// Performs SSH_FXP_CLOSE request.
+ /// Performs a SSH_FXP_CLOSE request.
///
/// The handle.
void RequestClose(byte[] handle);
+ ///
+ /// Performs a SSH_FXP_CLOSE request.
+ ///
+ /// The handle.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_CLOSE request.
+ ///
Task RequestCloseAsync(byte[] handle, CancellationToken cancellationToken);
///
@@ -365,6 +486,18 @@ void RequestWrite(byte[] handle,
///
uint CalculateOptimalWriteLength(uint bufferSize, byte[] handle);
+ ///
+ /// Creates an for reading the content of the file represented by a given .
+ ///
+ /// The handle of the file to read.
+ /// The SFTP session.
+ /// The maximum number of bytes to read with each chunk.
+ /// The maximum number of pending reads.
+ /// The size of the file or when the size could not be determined.
+ ///
+ /// An for reading the content of the file represented by the
+ /// specified .
+ ///
ISftpFileReader CreateFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, int maxPendingReads, long? fileSize);
}
}
diff --git a/src/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs
index 8693929d5..59da98af5 100644
--- a/src/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs
+++ b/src/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs
@@ -15,7 +15,10 @@ public override SftpMessageTypes SftpMessageType
public string Name
{
- get { return _name; }
+ get
+ {
+ return _name;
+ }
private set
{
_name = value;
@@ -52,4 +55,4 @@ protected override void SaveData()
WriteBinaryString(_nameBytes);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs b/src/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs
index 768764419..d7cecf8f9 100644
--- a/src/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs
+++ b/src/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs
@@ -18,8 +18,7 @@ public override void LoadData(SshDataStream stream)
stream.ReadUInt64(), // AvailableNodes
stream.ReadUInt64(), // Sid
stream.ReadUInt64(), // Flags
- stream.ReadUInt64() // MaxNameLenght
- );
+ stream.ReadUInt64()); // MaxNameLenght
}
}
}
diff --git a/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs
index ce0d414c5..04a7d4d82 100644
--- a/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs
+++ b/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs
@@ -17,7 +17,7 @@ public SftpDataResponse(uint protocolVersion)
protected override void LoadData()
{
base.LoadData();
-
+
Data = ReadBinary();
}
diff --git a/src/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs
index bbd41680f..84fe93d35 100644
--- a/src/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs
+++ b/src/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs
@@ -12,7 +12,8 @@ public SftpExtendedReplyResponse(uint protocolVersion)
{
}
- public T GetReply() where T : ExtendedReplyInfo, new()
+ public T GetReply()
+ where T : ExtendedReplyInfo, new()
{
var result = new T();
result.LoadData(DataStream);
diff --git a/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs
index 31da2e937..21af8f835 100644
--- a/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs
+++ b/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs
@@ -17,7 +17,7 @@ public SftpHandleResponse(uint protocolVersion)
protected override void LoadData()
{
base.LoadData();
-
+
Handle = ReadBinary();
}
diff --git a/src/Renci.SshNet/Sftp/Responses/SftpResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpResponse.cs
index 3b784bf09..5e3ac145b 100644
--- a/src/Renci.SshNet/Sftp/Responses/SftpResponse.cs
+++ b/src/Renci.SshNet/Sftp/Responses/SftpResponse.cs
@@ -14,7 +14,7 @@ protected SftpResponse(uint protocolVersion)
protected override void LoadData()
{
base.LoadData();
-
+
ResponseId = ReadUInt32();
}
diff --git a/src/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs b/src/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs
index b2505b20a..9e706d9e0 100644
--- a/src/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs
+++ b/src/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs
@@ -6,7 +6,7 @@ namespace Renci.SshNet.Sftp
///
/// Encapsulates the results of an asynchronous download operation.
///
- public class SftpDownloadAsyncResult : AsyncResult
+ public class SftpDownloadAsyncResult : AsyncResult
{
///
/// Gets or sets a value indicating whether to cancel asynchronous download operation.
diff --git a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs
index 824a3a0fb..2f32b4796 100644
--- a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs
+++ b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs
@@ -13,6 +13,7 @@ namespace Renci.SshNet.Sftp
public class SftpFileAttributes
{
#pragma warning disable IDE1006 // Naming Styles
+#pragma warning disable SA1310 // Field names should not contain underscore
private const uint S_IFMT = 0xF000; // bitmask for the file type bitfields
private const uint S_IFSOCK = 0xC000; // socket
private const uint S_IFLNK = 0xA000; // symbolic link
@@ -33,6 +34,7 @@ public class SftpFileAttributes
private const uint S_IROTH = 0x0004; // others have read permission
private const uint S_IWOTH = 0x0002; // others have write permission
private const uint S_IXOTH = 0x0001; // others have execute permission
+#pragma warning restore SA1310 // Field names should not contain underscore
#pragma warning restore IDE1006 // Naming Styles
private readonly DateTime _originalLastAccessTimeUtc;
@@ -165,7 +167,7 @@ public DateTime LastWriteTime
/// Gets a value indicating whether file represents a socket.
///
///
- /// if file represents a socket; otherwise, .
+ /// if file represents a socket; otherwise, .
///
public bool IsSocket { get; private set; }
@@ -477,7 +479,7 @@ public void SetPermissions(short mode)
var modeBytes = mode.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0').ToCharArray();
- var permission = (modeBytes[0] & 0x0F) * 8 * 8 + (modeBytes[1] & 0x0F) * 8 + (modeBytes[2] & 0x0F);
+ var permission = ((modeBytes[0] & 0x0F) * 8 * 8) + ((modeBytes[1] & 0x0F) * 8) + (modeBytes[2] & 0x0F);
OwnerCanRead = (permission & S_IRUSR) == S_IRUSR;
OwnerCanWrite = (permission & S_IWUSR) == S_IWUSR;
@@ -500,79 +502,88 @@ public void SetPermissions(short mode)
///
public byte[] GetBytes()
{
- var stream = new SshDataStream(4);
-
- uint flag = 0;
-
- if (IsSizeChanged && IsRegularFile)
+ using (var stream = new SshDataStream(4))
{
- flag |= 0x00000001;
- }
+ uint flag = 0;
- if (IsUserIdChanged || IsGroupIdChanged)
- {
- flag |= 0x00000002;
- }
+ if (IsSizeChanged && IsRegularFile)
+ {
+ flag |= 0x00000001;
+ }
- if (IsPermissionsChanged)
- {
- flag |= 0x00000004;
- }
+ if (IsUserIdChanged || IsGroupIdChanged)
+ {
+ flag |= 0x00000002;
+ }
- if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
- {
- flag |= 0x00000008;
- }
+ if (IsPermissionsChanged)
+ {
+ flag |= 0x00000004;
+ }
- if (IsExtensionsChanged)
- {
- flag |= 0x80000000;
- }
+ if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
+ {
+ flag |= 0x00000008;
+ }
- stream.Write(flag);
+ if (IsExtensionsChanged)
+ {
+ flag |= 0x80000000;
+ }
- if (IsSizeChanged && IsRegularFile)
- {
- stream.Write((ulong) Size);
- }
+ stream.Write(flag);
- if (IsUserIdChanged || IsGroupIdChanged)
- {
- stream.Write((uint) UserId);
- stream.Write((uint) GroupId);
- }
+ if (IsSizeChanged && IsRegularFile)
+ {
+ stream.Write((ulong) Size);
+ }
- if (IsPermissionsChanged)
- {
- stream.Write(Permissions);
- }
+ if (IsUserIdChanged || IsGroupIdChanged)
+ {
+ stream.Write((uint) UserId);
+ stream.Write((uint) GroupId);
+ }
- if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
- {
- var time = (uint)(LastAccessTimeUtc.ToFileTimeUtc() / 10000000 - 11644473600);
- stream.Write(time);
- time = (uint)(LastWriteTimeUtc.ToFileTimeUtc() / 10000000 - 11644473600);
- stream.Write(time);
- }
+ if (IsPermissionsChanged)
+ {
+ stream.Write(Permissions);
+ }
- if (IsExtensionsChanged)
- {
- foreach (var item in Extensions)
+ if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
{
- // TODO: we write as ASCII but read as UTF8 !!!
+ var time = (uint) ((LastAccessTimeUtc.ToFileTimeUtc() / 10000000) - 11644473600);
+ stream.Write(time);
+ time = (uint) ((LastWriteTimeUtc.ToFileTimeUtc() / 10000000) - 11644473600);
+ stream.Write(time);
+ }
- stream.Write(item.Key, SshData.Ascii);
- stream.Write(item.Value, SshData.Ascii);
+ if (IsExtensionsChanged)
+ {
+ foreach (var item in Extensions)
+ {
+ /*
+ * TODO: we write as ASCII but read as UTF8 !!!
+ */
+
+ stream.Write(item.Key, SshData.Ascii);
+ stream.Write(item.Value, SshData.Ascii);
+ }
}
- }
- return stream.ToArray();
+ return stream.ToArray();
+ }
}
internal static readonly SftpFileAttributes Empty = new SftpFileAttributes();
internal static SftpFileAttributes FromBytes(SshDataStream stream)
{
+ const uint SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+ const uint SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
+ const uint SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+ const uint SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008;
+ const uint SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+
var flag = stream.ReadUInt32();
long size = -1;
@@ -583,24 +594,24 @@ internal static SftpFileAttributes FromBytes(SshDataStream stream)
DateTime modifyTime;
Dictionary extensions = null;
- if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE
+ if ((flag & SSH_FILEXFER_ATTR_SIZE) == SSH_FILEXFER_ATTR_SIZE)
{
size = (long) stream.ReadUInt64();
}
- if ((flag & 0x00000002) == 0x00000002) // SSH_FILEXFER_ATTR_UIDGID
+ if ((flag & SSH_FILEXFER_ATTR_UIDGID) == SSH_FILEXFER_ATTR_UIDGID)
{
userId = (int) stream.ReadUInt32();
groupId = (int) stream.ReadUInt32();
}
- if ((flag & 0x00000004) == 0x00000004) // SSH_FILEXFER_ATTR_PERMISSIONS
+ if ((flag & SSH_FILEXFER_ATTR_PERMISSIONS) == SSH_FILEXFER_ATTR_PERMISSIONS)
{
permissions = stream.ReadUInt32();
}
- if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME
+ if ((flag & SSH_FILEXFER_ATTR_ACMODTIME) == SSH_FILEXFER_ATTR_ACMODTIME)
{
// The incoming times are "Unix times", so they're already in UTC. We need to preserve that
// to avoid losing information in a local time conversion during the "fall back" hour in DST.
@@ -615,7 +626,7 @@ internal static SftpFileAttributes FromBytes(SshDataStream stream)
modifyTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
}
- if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_EXTENDED
+ if ((flag & SSH_FILEXFER_ATTR_EXTENDED) == SSH_FILEXFER_ATTR_EXTENDED)
{
var extendedCount = (int) stream.ReadUInt32();
extensions = new Dictionary(extendedCount);
diff --git a/src/Renci.SshNet/Sftp/SftpFileReader.cs b/src/Renci.SshNet/Sftp/SftpFileReader.cs
index e10107b15..af32f87a7 100644
--- a/src/Renci.SshNet/Sftp/SftpFileReader.cs
+++ b/src/Renci.SshNet/Sftp/SftpFileReader.cs
@@ -74,10 +74,14 @@ public SftpFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, i
public byte[] Read()
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_disposingOrDisposed, this);
+#else
if (_disposingOrDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
+#endif // NET7_0_OR_GREATER
if (_exception is not null)
{
@@ -168,7 +172,9 @@ public byte[] Read()
var bytesToCatchUp = nextChunk.Offset - _offset;
- // TODO: break loop and interrupt blocking wait in case of exception
+ /*
+ * TODO: break loop and interrupt blocking wait in case of exception
+ */
var read = _sftpSession.RequestRead(_handle, _offset, (uint) bytesToCatchUp);
if (read.Length == 0)
diff --git a/src/Renci.SshNet/Sftp/SftpFileStream.cs b/src/Renci.SshNet/Sftp/SftpFileStream.cs
index be1e756ac..03b9e088f 100644
--- a/src/Renci.SshNet/Sftp/SftpFileStream.cs
+++ b/src/Renci.SshNet/Sftp/SftpFileStream.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -12,7 +13,9 @@ namespace Renci.SshNet.Sftp
/// Exposes a around a remote SFTP file, supporting both synchronous and asynchronous read and write operations.
///
///
+#pragma warning disable CA1844 // Provide memory-based overrides of async methods when subclassing 'Stream'
public class SftpFileStream : Stream
+#pragma warning restore CA1844 // Provide memory-based overrides of async methods when subclassing 'Stream'
{
private readonly object _lock = new object();
private readonly int _readBufferSize;
@@ -181,9 +184,9 @@ private SftpFileStream(ISftpSession session, string path, FileAccess access, int
Name = path;
_session = session;
- _canRead = (access & FileAccess.Read) != 0;
+ _canRead = (access & FileAccess.Read) == FileAccess.Read;
_canSeek = true;
- _canWrite = (access & FileAccess.Write) != 0;
+ _canWrite = (access & FileAccess.Write) == FileAccess.Write;
_handle = handle;
@@ -221,9 +224,9 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc
// Initialize the object state.
_session = session;
- _canRead = (access & FileAccess.Read) != 0;
+ _canRead = (access & FileAccess.Read) == FileAccess.Read;
_canSeek = true;
- _canWrite = (access & FileAccess.Write) != 0;
+ _canWrite = (access & FileAccess.Write) == FileAccess.Write;
var flags = Flags.None;
@@ -243,20 +246,25 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc
throw new ArgumentOutOfRangeException(nameof(access));
}
- if ((access & FileAccess.Read) != 0 && mode == FileMode.Append)
+ if ((access & FileAccess.Read) == FileAccess.Read && mode == FileMode.Append)
{
- throw new ArgumentException(string.Format("{0} mode can be requested only when combined with write-only access.", mode.ToString("G")));
+ throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
+ "{0} mode can be requested only when combined with write-only access.",
+ mode.ToString("G")),
+ nameof(mode));
}
- if ((access & FileAccess.Write) == 0)
+ if ((access & FileAccess.Write) != FileAccess.Write)
{
if (mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append)
{
- throw new ArgumentException(string.Format("Combining {0}: {1} with {2}: {3} is invalid.",
+ throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
+ "Combining {0}: {1} with {2}: {3} is invalid.",
nameof(FileMode),
mode,
nameof(FileAccess),
- access));
+ access),
+ nameof(mode));
}
}
@@ -345,20 +353,25 @@ internal static async Task OpenAsync(ISftpSession session, strin
throw new ArgumentOutOfRangeException(nameof(access));
}
- if ((access & FileAccess.Read) != 0 && mode == FileMode.Append)
+ if ((access & FileAccess.Read) == FileAccess.Read && mode == FileMode.Append)
{
- throw new ArgumentException(string.Format("{0} mode can be requested only when combined with write-only access.", mode.ToString("G")));
+ throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
+ "{0} mode can be requested only when combined with write-only access.",
+ mode.ToString("G")),
+ nameof(mode));
}
- if ((access & FileAccess.Write) == 0)
+ if ((access & FileAccess.Write) != FileAccess.Write)
{
if (mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append)
{
- throw new ArgumentException(string.Format("Combining {0}: {1} with {2}: {3} is invalid.",
+ throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
+ "Combining {0}: {1} with {2}: {3} is invalid.",
nameof(FileMode),
mode,
nameof(FileAccess),
- access));
+ access),
+ nameof(mode));
}
}
@@ -1318,10 +1331,14 @@ private void SetupWrite()
private void CheckSessionIsOpen()
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_session is null, this);
+#else
if (_session is null)
{
throw new ObjectDisposedException(GetType().FullName);
}
+#endif // NET7_0_OR_GREATER
if (!_session.IsOpen)
{
diff --git a/src/Renci.SshNet/Sftp/SftpFileSystemInformation.cs b/src/Renci.SshNet/Sftp/SftpFileSystemInformation.cs
index 61f32e1de..89d24a727 100644
--- a/src/Renci.SshNet/Sftp/SftpFileSystemInformation.cs
+++ b/src/Renci.SshNet/Sftp/SftpFileSystemInformation.cs
@@ -5,10 +5,14 @@ namespace Renci.SshNet.Sftp
///
/// Contains File system information exposed by statvfs@openssh.com request.
///
+#pragma warning disable SA1649 // File name should match first type name
public class SftpFileSytemInformation
+#pragma warning restore SA1649 // File name should match first type name
{
+#pragma warning disable SA1310 // Field names should not contain underscore
internal const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1;
internal const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2;
+#pragma warning restore SA1310 // Field names should not contain underscore
private readonly ulong _flag;
@@ -99,7 +103,7 @@ public bool IsReadOnly
/// Gets a value indicating whether [supports set uid].
///
///
- /// if [supports set uid]; otherwise, .
+ /// if [supports set uid]; otherwise, .
///
public bool SupportsSetUid
{
@@ -158,4 +162,4 @@ internal void SaveData(SshDataStream stream)
stream.Write(MaxNameLenght);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs
index efca5d06f..0fec92230 100644
--- a/src/Renci.SshNet/Sftp/SftpSession.cs
+++ b/src/Renci.SshNet/Sftp/SftpSession.cs
@@ -11,6 +11,9 @@
namespace Renci.SshNet.Sftp
{
+ ///
+ /// Represents an SFTP session.
+ ///
internal sealed class SftpSession : SubsystemSession, ISftpSession
{
internal const int MaximumSupportedVersion = 3;
@@ -114,14 +117,22 @@ public string GetCanonicalPath(string path)
if (fullPath.EndsWith("/.", StringComparison.OrdinalIgnoreCase) ||
fullPath.EndsWith("/..", StringComparison.OrdinalIgnoreCase) ||
fullPath.Equals("/", StringComparison.OrdinalIgnoreCase) ||
+#if NET || NETSTANDARD2_1_OR_GREATER
+ fullPath.IndexOf('/', StringComparison.OrdinalIgnoreCase) < 0)
+#else
fullPath.IndexOf('/') < 0)
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
return fullPath;
}
var pathParts = fullPath.Split('/');
+#if NET || NETSTANDARD2_1_OR_GREATER
+ var partialFullPath = string.Join('/', pathParts, 0, pathParts.Length - 1);
+#else
var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1);
+#endif // NET || NETSTANDARD2_1_OR_GREATER
if (string.IsNullOrEmpty(partialFullPath))
{
@@ -149,6 +160,14 @@ public string GetCanonicalPath(string path)
return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", canonizedPath, slash, pathParts[pathParts.Length - 1]);
}
+ ///
+ /// Asynchronously resolves a given path into an absolute path on the server.
+ ///
+ /// The path to resolve.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task representing the absolute path.
+ ///
public async Task GetCanonicalPathAsync(string path, CancellationToken cancellationToken)
{
var fullPath = GetFullRemotePath(path);
@@ -169,14 +188,22 @@ public async Task GetCanonicalPathAsync(string path, CancellationToken c
if (fullPath.EndsWith("/.", StringComparison.Ordinal) ||
fullPath.EndsWith("/..", StringComparison.Ordinal) ||
fullPath.Equals("/", StringComparison.Ordinal) ||
+#if NET || NETSTANDARD2_1_OR_GREATER
+ fullPath.IndexOf('/', StringComparison.Ordinal) < 0)
+#else
fullPath.IndexOf('/') < 0)
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
return fullPath;
}
var pathParts = fullPath.Split('/');
- var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1);
+#if NET || NETSTANDARD2_1_OR_GREATER
+ var partialFullPath = string.Join('/', pathParts);
+#else
+ var partialFullPath = string.Join("/", pathParts);
+#endif // NET || NETSTANDARD2_1_OR_GREATER
if (string.IsNullOrEmpty(partialFullPath))
{
@@ -204,6 +231,18 @@ public async Task GetCanonicalPathAsync(string path, CancellationToken c
return canonizedPath + slash + pathParts[pathParts.Length - 1];
}
+ ///
+ /// Creates an for reading the content of the file represented by a given .
+ ///
+ /// The handle of the file to read.
+ /// The SFTP session.
+ /// The maximum number of bytes to read with each chunk.
+ /// The maximum number of pending reads.
+ /// The size of the file or when the size could not be determined.
+ ///
+ /// An for reading the content of the file represented by the
+ /// specified .
+ ///
public ISftpFileReader CreateFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, int maxPendingReads, long? fileSize)
{
return new SftpFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize);
@@ -224,6 +263,7 @@ internal string GetFullRemotePath(string path)
fullPath = WorkingDirectory + '/' + path;
}
}
+
return fullPath;
}
@@ -417,7 +457,7 @@ private void SendRequest(SftpRequest request)
///
/// The path.
/// The flags.
- /// if set to returns instead of throwing an exception.
+ /// If set to returns instead of throwing an exception.
/// File handle.
public byte[] RequestOpen(string path, Flags flags, bool nullOnError = false)
{
@@ -432,44 +472,61 @@ public byte[] RequestOpen(string path, Flags flags, bool nullOnError = false)
_encoding,
flags,
response =>
- {
- handle = response.Handle;
- _ = wait.Set();
- },
+ {
+ handle = response.Handle;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (!nullOnError && exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (!nullOnError && exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
+#pragma warning restore CA1508 // Avoid dead conditional code
return handle;
}
+ ///
+ /// Asynchronously performs a SSH_FXP_OPEN request.
+ ///
+ /// The path.
+ /// The flags.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_OPEN request. The value of its
+ /// contains the file handle of the specified path.
+ ///
public async Task RequestOpenAsync(string path, Flags flags, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpOpenRequest(ProtocolVersion,
- NextRequestId,
- path,
- _encoding,
- flags,
- response => tcs.TrySetResult(response.Handle),
- response => tcs.TrySetException(GetSftpException(response))));
+ NextRequestId,
+ path,
+ _encoding,
+ flags,
+ response => tcs.TrySetResult(response.Handle),
+ response => tcs.TrySetException(GetSftpException(response))));
return await tcs.Task.ConfigureAwait(false);
}
@@ -495,13 +552,13 @@ public SftpOpenAsyncResult BeginOpen(string path, Flags flags, AsyncCallback cal
_encoding,
flags,
response =>
- {
- asyncResult.SetAsCompleted(response.Handle, completedSynchronously: false);
- },
+ {
+ asyncResult.SetAsCompleted(response.Handle, completedSynchronously: false);
+ },
response =>
- {
- asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false);
- });
+ {
+ asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false);
+ });
SendRequest(request);
@@ -558,22 +615,32 @@ public void RequestClose(byte[] handle)
NextRequestId,
handle,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
}
+ ///
+ /// Performs a SSH_FXP_CLOSE request.
+ ///
+ /// The handle.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_CLOSE request.
+ ///
public async Task RequestCloseAsync(byte[] handle, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
@@ -582,21 +649,25 @@ public async Task RequestCloseAsync(byte[] handle, CancellationToken cancellatio
NextRequestId,
handle,
response =>
+ {
+ if (response.StatusCode == StatusCodes.Ok)
{
- if (response.StatusCode == StatusCodes.Ok)
- {
- _ = tcs.TrySetResult(true);
- }
- else
- {
- _ = tcs.TrySetException(GetSftpException(response));
- }
- }));
+ _ = tcs.TrySetResult(true);
+ }
+ else
+ {
+ _ = tcs.TrySetException(GetSftpException(response));
+ }
+ }));
// Only check for cancellation after the SftpCloseRequest was sent
cancellationToken.ThrowIfCancellationRequested();
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
_ = await tcs.Task.ConfigureAwait(false);
}
@@ -619,9 +690,9 @@ public SftpCloseAsyncResult BeginClose(byte[] handle, AsyncCallback callback, ob
NextRequestId,
handle,
response =>
- {
- asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false);
- });
+ {
+ asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false);
+ });
SendRequest(request);
return asyncResult;
@@ -740,7 +811,9 @@ public byte[] EndRead(SftpReadAsyncResult asyncResult)
/// The handle.
/// The offset.
/// The length.
- /// data array; null if EOF
+ ///
+ /// The data that was read, or an empty array when the end of the file was reached.
+ ///
public byte[] RequestRead(byte[] handle, ulong offset, uint length)
{
SshException exception = null;
@@ -755,30 +828,32 @@ public byte[] RequestRead(byte[] handle, ulong offset, uint length)
offset,
length,
response =>
- {
- data = response.Data;
- _ = wait.Set();
- },
+ {
+ data = response.Data;
+ _ = wait.Set();
+ },
response =>
+ {
+ if (response.StatusCode != StatusCodes.Eof)
{
- if (response.StatusCode != StatusCodes.Eof)
- {
- exception = GetSftpException(response);
- }
- else
- {
- data = Array.Empty();
- }
+ exception = GetSftpException(response);
+ }
+ else
+ {
+ data = Array.Empty();
+ }
- _ = wait.Set();
- });
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -786,13 +861,29 @@ public byte[] RequestRead(byte[] handle, ulong offset, uint length)
return data;
}
+ ///
+ /// Asynchronously performs a SSH_FXP_READ request.
+ ///
+ /// The handle to the file to read from.
+ /// The offset in the file to start reading from.
+ /// The number of bytes to read.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_READ request. The value of
+ /// its contains the data read from the file, or an empty
+ /// array when the end of the file is reached.
+ ///
public async Task RequestReadAsync(byte[] handle, ulong offset, uint length, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpReadRequest(ProtocolVersion,
NextRequestId,
@@ -844,36 +935,54 @@ public void RequestWrite(byte[] handle,
offset,
length,
response =>
- {
- writeCompleted?.Invoke(response);
+ {
+ writeCompleted?.Invoke(response);
- exception = GetSftpException(response);
- if (wait != null)
- {
- _ = wait.Set();
- }
- });
+ exception = GetSftpException(response);
+ if (wait != null)
+ {
+ _ = wait.Set();
+ }
+ });
SendRequest(request);
- if (wait != null)
+ if (wait is not null)
{
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
}
+ ///
+ /// Asynchronouly performs a SSH_FXP_WRITE request.
+ ///
+ /// The handle.
+ /// The the zero-based offset (in bytes) relative to the beginning of the file that the write must start at.
+ /// The buffer holding the data to write.
+ /// the zero-based offset in at which to begin taking bytes to write.
+ /// The length (in bytes) of the data to write.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_WRITE request.
+ ///
public async Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpWriteRequest(ProtocolVersion,
NextRequestId,
@@ -883,16 +992,16 @@ public async Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] da
offset,
length,
response =>
+ {
+ if (response.StatusCode == StatusCodes.Ok)
{
- if (response.StatusCode == StatusCodes.Ok)
- {
- _ = tcs.TrySetResult(true);
- }
- else
- {
- _ = tcs.TrySetException(GetSftpException(response));
- }
- }));
+ _ = tcs.TrySetResult(true);
+ }
+ else
+ {
+ _ = tcs.TrySetException(GetSftpException(response));
+ }
+ }));
_ = await tcs.Task.ConfigureAwait(false);
}
@@ -917,22 +1026,24 @@ public SftpFileAttributes RequestLStat(string path)
path,
_encoding,
response =>
- {
- attributes = response.Attributes;
- _ = wait.Set();
- },
+ {
+ attributes = response.Attributes;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -958,13 +1069,13 @@ public SFtpStatAsyncResult BeginLStat(string path, AsyncCallback callback, objec
path,
_encoding,
response =>
- {
- asyncResult.SetAsCompleted(response.Attributes, completedSynchronously: false);
- },
+ {
+ asyncResult.SetAsCompleted(response.Attributes, completedSynchronously: false);
+ },
response =>
- {
- asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false);
- });
+ {
+ asyncResult.SetAsCompleted(GetSftpException(response), completedSynchronously: false);
+ });
SendRequest(request);
return asyncResult;
@@ -1006,7 +1117,7 @@ public SftpFileAttributes EndLStat(SFtpStatAsyncResult asyncResult)
/// Performs SSH_FXP_FSTAT request.
///
/// The handle.
- /// if set to returns instead of throwing an exception.
+ /// If set to , returns instead of throwing an exception.
///
/// File attributes.
///
@@ -1021,22 +1132,24 @@ public SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError)
NextRequestId,
handle,
response =>
- {
- attributes = response.Attributes;
- _ = wait.Set();
- },
+ {
+ attributes = response.Attributes;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null && !nullOnError)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (!nullOnError && exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1044,13 +1157,26 @@ public SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError)
return attributes;
}
+ ///
+ /// Asynchronously performs a SSH_FXP_FSTAT request.
+ ///
+ /// The handle.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_FSTAT request. The value of its
+ /// contains the file attributes of the specified handle.
+ ///
public async Task RequestFStatAsync(byte[] handle, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpFStatRequest(ProtocolVersion,
NextRequestId,
@@ -1079,17 +1205,19 @@ public void RequestSetStat(string path, SftpFileAttributes attributes)
_encoding,
attributes,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1111,17 +1239,19 @@ public void RequestFSetStat(byte[] handle, SftpFileAttributes attributes)
handle,
attributes,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1131,7 +1261,7 @@ public void RequestFSetStat(byte[] handle, SftpFileAttributes attributes)
/// Performs SSH_FXP_OPENDIR request.
///
/// The path.
- /// if set to returns instead of throwing an exception.
+ /// If set to , returns instead of throwing an exception.
/// File handle.
public byte[] RequestOpenDir(string path, bool nullOnError = false)
{
@@ -1146,22 +1276,24 @@ public byte[] RequestOpenDir(string path, bool nullOnError = false)
path,
_encoding,
response =>
- {
- handle = response.Handle;
- _ = wait.Set();
- },
+ {
+ handle = response.Handle;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (!nullOnError && exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (!nullOnError && exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1169,13 +1301,26 @@ public byte[] RequestOpenDir(string path, bool nullOnError = false)
return handle;
}
+ ///
+ /// Asynchronously performs a SSH_FXP_OPENDIR request.
+ ///
+ /// The path.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_OPENDIR request. The value of its
+ /// contains the handle of the specified path.
+ ///
public async Task RequestOpenDirAsync(string path, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpOpenDirRequest(ProtocolVersion,
NextRequestId,
@@ -1191,8 +1336,11 @@ public async Task RequestOpenDirAsync(string path, CancellationToken can
///
/// Performs SSH_FXP_READDIR request.
///
- /// The handle.
- ///
+ /// The handle of the directory to read.
+ ///
+ /// A where the key is the name of a file in
+ /// the directory and the value is the of the file.
+ ///
public KeyValuePair[] RequestReadDir(byte[] handle)
{
SshException exception = null;
@@ -1205,26 +1353,28 @@ public KeyValuePair[] RequestReadDir(byte[] handle)
NextRequestId,
handle,
response =>
- {
- result = response.Files;
- _ = wait.Set();
- },
+ {
+ result = response.Files;
+ _ = wait.Set();
+ },
response =>
+ {
+ if (response.StatusCode != StatusCodes.Eof)
{
- if (response.StatusCode != StatusCodes.Eof)
- {
- exception = GetSftpException(response);
- }
+ exception = GetSftpException(response);
+ }
- _ = wait.Set();
- });
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1232,29 +1382,44 @@ public KeyValuePair[] RequestReadDir(byte[] handle)
return result;
}
+ ///
+ /// Performs a SSH_FXP_READDIR request.
+ ///
+ /// The handle of the directory to read.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_READDIR request. The value of its
+ /// contains a where the
+ /// key is the name of a file in the directory and the value is the
+ /// of the file.
+ ///
public async Task[]> RequestReadDirAsync(byte[] handle, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource[]>(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource[]>) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource[]>) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource[]>) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpReadDirRequest(ProtocolVersion,
NextRequestId,
handle,
response => tcs.TrySetResult(response.Files),
response =>
+ {
+ if (response.StatusCode == StatusCodes.Eof)
{
- if (response.StatusCode == StatusCodes.Eof)
- {
- _ = tcs.TrySetResult(null);
- }
- else
- {
- _ = tcs.TrySetException(GetSftpException(response));
- }
- }));
+ _ = tcs.TrySetResult(null);
+ }
+ else
+ {
+ _ = tcs.TrySetException(GetSftpException(response));
+ }
+ }));
return await tcs.Task.ConfigureAwait(false);
}
@@ -1275,45 +1440,59 @@ public void RequestRemove(string path)
path,
_encoding,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
}
+ ///
+ /// Asynchronously performs a SSH_FXP_REMOVE request.
+ ///
+ /// The path.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_REMOVE request.
+ ///
public async Task RequestRemoveAsync(string path, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpRemoveRequest(ProtocolVersion,
NextRequestId,
path,
_encoding,
response =>
+ {
+ if (response.StatusCode == StatusCodes.Ok)
{
- if (response.StatusCode == StatusCodes.Ok)
- {
- _ = tcs.TrySetResult(true);
- }
- else
- {
- _ = tcs.TrySetException(GetSftpException(response));
- }
- }));
+ _ = tcs.TrySetResult(true);
+ }
+ else
+ {
+ _ = tcs.TrySetException(GetSftpException(response));
+ }
+ }));
_ = await tcs.Task.ConfigureAwait(false);
}
@@ -1334,17 +1513,19 @@ public void RequestMkDir(string path)
path,
_encoding,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1365,17 +1546,19 @@ public void RequestRmDir(string path)
path,
_encoding,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1402,22 +1585,24 @@ internal KeyValuePair[] RequestRealPath(string path,
path,
_encoding,
response =>
- {
- result = response.Files;
- _ = wait.Set();
- },
+ {
+ result = response.Files;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (!nullOnError && exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (!nullOnError && exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1431,7 +1616,11 @@ internal async Task[]> RequestRealPathA
var tcs = new TaskCompletionSource[]>(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource[]>) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource[]>) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource[]>) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpRealPathRequest(ProtocolVersion,
NextRequestId,
@@ -1439,15 +1628,15 @@ internal async Task[]> RequestRealPathA
_encoding,
response => tcs.TrySetResult(response.Files),
response =>
+ {
+ if (nullOnError)
{
- if (nullOnError)
- {
- _ = tcs.TrySetResult(null);
- }
- else
- {
- _ = tcs.TrySetException(GetSftpException(response));
- }
+ _ = tcs.TrySetResult(null);
+ }
+ else
+ {
+ _ = tcs.TrySetException(GetSftpException(response));
+ }
}));
return await tcs.Task.ConfigureAwait(false);
@@ -1531,22 +1720,24 @@ public SftpFileAttributes RequestStat(string path, bool nullOnError = false)
path,
_encoding,
response =>
- {
- attributes = response.Attributes;
- _ = wait.Set();
- },
+ {
+ attributes = response.Attributes;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (!nullOnError && exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (!nullOnError && exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1632,29 +1823,44 @@ public void RequestRename(string oldPath, string newPath)
newPath,
_encoding,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
}
+ ///
+ /// Asynchronously performs a SSH_FXP_RENAME request.
+ ///
+ /// The old path.
+ /// The new path.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the asynchronous SSH_FXP_RENAME request.
+ ///
public async Task RequestRenameAsync(string oldPath, string newPath, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new SftpRenameRequest(ProtocolVersion,
NextRequestId,
@@ -1662,16 +1868,16 @@ public async Task RequestRenameAsync(string oldPath, string newPath, Cancellatio
newPath,
_encoding,
response =>
+ {
+ if (response.StatusCode == StatusCodes.Ok)
{
- if (response.StatusCode == StatusCodes.Ok)
- {
- _ = tcs.TrySetResult(true);
- }
- else
- {
- _ = tcs.TrySetException(GetSftpException(response));
- }
- }));
+ _ = tcs.TrySetResult(true);
+ }
+ else
+ {
+ _ = tcs.TrySetException(GetSftpException(response));
+ }
+ }));
_ = await tcs.Task.ConfigureAwait(false);
}
@@ -1681,8 +1887,11 @@ public async Task RequestRenameAsync(string oldPath, string newPath, Cancellatio
/// Performs SSH_FXP_READLINK request.
///
/// The path.
- /// if set to returns null instead of throwing an exception.
- ///
+ /// if set to returns instead of throwing an exception.
+ ///
+ /// An array of where the key is the name of
+ /// a file and the value is the of the file.
+ ///
internal KeyValuePair[] RequestReadLink(string path, bool nullOnError = false)
{
if (ProtocolVersion < 3)
@@ -1701,22 +1910,24 @@ internal KeyValuePair[] RequestReadLink(string path,
path,
_encoding,
response =>
- {
- result = response.Files;
- _ = wait.Set();
- },
+ {
+ result = response.Files;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (!nullOnError && exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (!nullOnError && exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1746,17 +1957,19 @@ public void RequestSymLink(string linkpath, string targetpath)
targetpath,
_encoding,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
SendRequest(request);
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1784,10 +1997,10 @@ public void RequestPosixRename(string oldPath, string newPath)
newPath,
_encoding,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
if (!_supportedExtensions.ContainsKey(request.Name))
{
@@ -1799,7 +2012,9 @@ public void RequestPosixRename(string oldPath, string newPath)
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1831,15 +2046,15 @@ public SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = f
path,
_encoding,
response =>
- {
- information = response.GetReply().Information;
- _ = wait.Set();
- },
+ {
+ information = response.GetReply().Information;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
if (!_supportedExtensions.ContainsKey(request.Name))
{
@@ -1851,7 +2066,9 @@ public SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = f
WaitOnHandle(wait, OperationTimeout);
}
- if (!nullOnError && exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (!nullOnError && exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1859,6 +2076,16 @@ public SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = f
return information;
}
+ ///
+ /// Asynchronously performs a statvfs@openssh.com extended request.
+ ///
+ /// The path.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that represents the statvfs@openssh.com extended request. The value of its
+ /// contains the file system information for the specified
+ /// path.
+ ///
public async Task RequestStatVfsAsync(string path, CancellationToken cancellationToken)
{
if (ProtocolVersion < 3)
@@ -1870,7 +2097,11 @@ public async Task RequestStatVfsAsync(string path, Can
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- using (cancellationToken.Register((s) => ((TaskCompletionSource) s).TrySetCanceled(), tcs, useSynchronizationContext: false))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ await using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+ using (cancellationToken.Register(s => ((TaskCompletionSource) s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
SendRequest(new StatVfsRequest(ProtocolVersion,
NextRequestId,
@@ -1909,15 +2140,15 @@ internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnErro
NextRequestId,
handle,
response =>
- {
- information = response.GetReply().Information;
- _ = wait.Set();
- },
+ {
+ information = response.GetReply().Information;
+ _ = wait.Set();
+ },
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
if (!_supportedExtensions.ContainsKey(request.Name))
{
@@ -1929,7 +2160,9 @@ internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnErro
WaitOnHandle(wait, OperationTimeout);
}
- if (!nullOnError && exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (!nullOnError && exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -1958,10 +2191,10 @@ internal void HardLink(string oldPath, string newPath)
oldPath,
newPath,
response =>
- {
- exception = GetSftpException(response);
- _ = wait.Set();
- });
+ {
+ exception = GetSftpException(response);
+ _ = wait.Set();
+ });
if (!_supportedExtensions.ContainsKey(request.Name))
{
@@ -1973,7 +2206,9 @@ internal void HardLink(string oldPath, string newPath)
WaitOnHandle(wait, OperationTimeout);
}
- if (exception != null)
+#pragma warning disable CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
+ if (exception is not null)
+#pragma warning restore CA1508 // Avoid dead conditional code; Remove when we upgrade to newer SDK in which bug is fixed
{
throw exception;
}
@@ -2033,7 +2268,7 @@ public uint CalculateOptimalWriteLength(uint bufferSize, byte[] handle)
* WinSCP uses data length of 32739 bytes (total 32768 bytes; 32739 + 25 + 4 bytes for handle)
*/
- var lengthOfNonDataProtocolFields = 25u + (uint)handle.Length;
+ var lengthOfNonDataProtocolFields = 25u + (uint) handle.Length;
var maximumPacketSize = Channel.RemotePacketSize;
return Math.Min(bufferSize, maximumPacketSize) - lengthOfNonDataProtocolFields;
}
@@ -2061,7 +2296,7 @@ private void HandleResponse(SftpResponse response)
lock (_requests)
{
_ = _requests.TryGetValue(response.ResponseId, out request);
- if (request != null)
+ if (request is not null)
{
_ = _requests.Remove(response.ResponseId);
}
diff --git a/src/Renci.SshNet/Sftp/StatusCodes.cs b/src/Renci.SshNet/Sftp/StatusCodes.cs
index 101b8c0bc..12997a47e 100644
--- a/src/Renci.SshNet/Sftp/StatusCodes.cs
+++ b/src/Renci.SshNet/Sftp/StatusCodes.cs
@@ -1,135 +1,165 @@
-
-namespace Renci.SshNet.Sftp
+namespace Renci.SshNet.Sftp
{
internal enum StatusCodes : uint
{
///
- /// SSH_FX_OK
+ /// SSH_FX_OK.
///
Ok = 0,
+
///
- /// SSH_FX_EOF
+ /// SSH_FX_EOF.
///
Eof = 1,
+
///
- /// SSH_FX_NO_SUCH_FILE
+ /// SSH_FX_NO_SUCH_FILE.
///
NoSuchFile = 2,
+
///
- /// SSH_FX_PERMISSION_DENIED
+ /// SSH_FX_PERMISSION_DENIED.
///
PermissionDenied = 3,
+
///
- /// SSH_FX_FAILURE
+ /// SSH_FX_FAILURE.
///
Failure = 4,
+
///
- /// SSH_FX_BAD_MESSAGE
+ /// SSH_FX_BAD_MESSAGE.
///
BadMessage = 5,
+
///
- /// SSH_FX_NO_CONNECTION
+ /// SSH_FX_NO_CONNECTION.
///
NoConnection = 6,
+
///
- /// SSH_FX_CONNECTION_LOST
+ /// SSH_FX_CONNECTION_LOST.
///
ConnectionLost = 7,
+
///
- /// SSH_FX_OP_UNSUPPORTED
+ /// SSH_FX_OP_UNSUPPORTED.
///
OperationUnsupported = 8,
+
///
- /// SSH_FX_INVALID_HANDLE
+ /// SSH_FX_INVALID_HANDLE.
///
InvalidHandle = 9,
+
///
- /// SSH_FX_NO_SUCH_PATH
+ /// SSH_FX_NO_SUCH_PATH.
///
NoSuchPath = 10,
+
///
- /// SSH_FX_FILE_ALREADY_EXISTS
+ /// SSH_FX_FILE_ALREADY_EXISTS.
///
FileAlreadyExists = 11,
+
///
- /// SSH_FX_WRITE_PROTECT
+ /// SSH_FX_WRITE_PROTECT.
///
WriteProtect = 12,
+
///
- /// SSH_FX_NO_MEDIA
+ /// SSH_FX_NO_MEDIA.
///
NoMedia = 13,
+
///
- /// SSH_FX_NO_SPACE_ON_FILESYSTEM
+ /// SSH_FX_NO_SPACE_ON_FILESYSTEM.
///
NoSpaceOnFilesystem = 14,
+
///
- /// SSH_FX_QUOTA_EXCEEDED
+ /// SSH_FX_QUOTA_EXCEEDED.
///
QuotaExceeded = 15,
+
///
- /// SSH_FX_UNKNOWN_PRINCIPAL
+ /// SSH_FX_UNKNOWN_PRINCIPAL.
///
UnknownPrincipal = 16,
+
///
- /// SSH_FX_LOCK_CONFLICT
+ /// SSH_FX_LOCK_CONFLICT.
///
LockConflict = 17,
+
///
- /// SSH_FX_DIR_NOT_EMPTY
+ /// SSH_FX_DIR_NOT_EMPTY.
///
DirNotEmpty = 18,
+
///
- /// SSH_FX_NOT_A_DIRECTORY
+ /// SSH_FX_NOT_A_DIRECTORY.
///
NotDirectory = 19,
+
///
- /// SSH_FX_INVALID_FILENAME
+ /// SSH_FX_INVALID_FILENAME.
///
InvalidFilename = 20,
+
///
- /// SSH_FX_LINK_LOOP
+ /// SSH_FX_LINK_LOOP.
///
LinkLoop = 21,
+
///
- /// SSH_FX_CANNOT_DELETE
+ /// SSH_FX_CANNOT_DELETE.
///
CannotDelete = 22,
+
///
- /// SSH_FX_INVALID_PARAMETER
+ /// SSH_FX_INVALID_PARAMETER.
///
InvalidParameter = 23,
+
///
- /// SSH_FX_FILE_IS_A_DIRECTORY
+ /// SSH_FX_FILE_IS_A_DIRECTORY.
///
FileIsADirectory = 24,
+
///
- /// SSH_FX_BYTE_RANGE_LOCK_CONFLICT
+ /// SSH_FX_BYTE_RANGE_LOCK_CONFLICT.
///
ByteRangeLockConflict = 25,
+
///
- /// SSH_FX_BYTE_RANGE_LOCK_REFUSED
+ /// SSH_FX_BYTE_RANGE_LOCK_REFUSED.
///
ByteRangeLockRefused = 26,
+
///
- /// SSH_FX_DELETE_PENDING
+ /// SSH_FX_DELETE_PENDING.
///
DeletePending = 27,
+
///
- /// SSH_FX_FILE_CORRUPT
+ /// SSH_FX_FILE_CORRUPT.
///
FileCorrupt = 28,
+
///
- /// SSH_FX_OWNER_INVALID
+ /// SSH_FX_OWNER_INVALID.
///
OwnerInvalid = 29,
+
///
- /// SSH_FX_GROUP_INVALID
+ /// SSH_FX_GROUP_INVALID.
///
GroupInvalid = 30,
+
///
- /// SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK
+ /// SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK.
///
- NoMatchingByteRangeLock = 31,
+ NoMatchingByteRangeLock = 31
}
}
diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs
index 5467ceb92..aa30efadb 100644
--- a/src/Renci.SshNet/SftpClient.cs
+++ b/src/Renci.SshNet/SftpClient.cs
@@ -1,16 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.IO;
using System.Globalization;
+using System.IO;
using System.Net;
+using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
+using System.Threading.Tasks;
+
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Sftp;
-using System.Threading.Tasks;
-using System.Runtime.CompilerServices;
namespace Renci.SshNet
{
@@ -213,7 +214,7 @@ public SftpClient(string host, string username, string password)
/// Authentication username.
/// Authentication private key file(s) .
/// is .
- /// is invalid. -or- is null or contains only whitespace characters.
+ /// is invalid. -or- is or contains only whitespace characters.
/// is not within and .
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
public SftpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
@@ -448,18 +449,17 @@ public void RenameFile(string oldPath, string newPath)
}
///
- /// Asynchronously renames remote file from old path to new path.
+ /// Renames remote file from old path to new path.
///
/// Path to the old file location.
/// Path to the new file location.
- /// The to observe.
- /// A that represents the asynchronous rename operation.
- /// is . -or- or is .
+ /// if set to then perform a posix rename.
+ /// is . -or- or is .
/// Client is not connected.
/// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server.
- /// A SSH error where is the message from the remote host.
+ /// A SSH error where is the message from the remote host.
/// The method was called after the client was disposed.
- public async Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken)
+ public void RenameFile(string oldPath, string newPath, bool isPosix)
{
CheckDisposed();
@@ -478,25 +478,33 @@ public async Task RenameFileAsync(string oldPath, string newPath, CancellationTo
throw new SshConnectionException("Client not connected.");
}
- cancellationToken.ThrowIfCancellationRequested();
+ var oldFullPath = _sftpSession.GetCanonicalPath(oldPath);
- var oldFullPath = await _sftpSession.GetCanonicalPathAsync(oldPath, cancellationToken).ConfigureAwait(false);
- var newFullPath = await _sftpSession.GetCanonicalPathAsync(newPath, cancellationToken).ConfigureAwait(false);
- await _sftpSession.RequestRenameAsync(oldFullPath, newFullPath, cancellationToken).ConfigureAwait(false);
+ var newFullPath = _sftpSession.GetCanonicalPath(newPath);
+
+ if (isPosix)
+ {
+ _sftpSession.RequestPosixRename(oldFullPath, newFullPath);
+ }
+ else
+ {
+ _sftpSession.RequestRename(oldFullPath, newFullPath);
+ }
}
///
- /// Renames remote file from old path to new path.
+ /// Asynchronously renames remote file from old path to new path.
///
/// Path to the old file location.
/// Path to the new file location.
- /// if set to then perform a posix rename.
- /// is . -or- or is .
+ /// The to observe.
+ /// A that represents the asynchronous rename operation.
+ /// is . -or- or is .
/// Client is not connected.
/// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server.
- /// A SSH error where is the message from the remote host.
+ /// A SSH error where is the message from the remote host.
/// The method was called after the client was disposed.
- public void RenameFile(string oldPath, string newPath, bool isPosix)
+ public async Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -515,18 +523,11 @@ public void RenameFile(string oldPath, string newPath, bool isPosix)
throw new SshConnectionException("Client not connected.");
}
- var oldFullPath = _sftpSession.GetCanonicalPath(oldPath);
-
- var newFullPath = _sftpSession.GetCanonicalPath(newPath);
+ cancellationToken.ThrowIfCancellationRequested();
- if (isPosix)
- {
- _sftpSession.RequestPosixRename(oldFullPath, newFullPath);
- }
- else
- {
- _sftpSession.RequestRename(oldFullPath, newFullPath);
- }
+ var oldFullPath = await _sftpSession.GetCanonicalPathAsync(oldPath, cancellationToken).ConfigureAwait(false);
+ var newFullPath = await _sftpSession.GetCanonicalPathAsync(newPath, cancellationToken).ConfigureAwait(false);
+ await _sftpSession.RequestRenameAsync(oldFullPath, newFullPath, cancellationToken).ConfigureAwait(false);
}
///
@@ -735,7 +736,7 @@ public ISftpFile Get(string path)
}
///
- /// Checks whether file or directory exists;
+ /// Checks whether file or directory exists.
///
/// The path.
///
@@ -762,22 +763,24 @@ public bool Exists(string path)
var fullPath = _sftpSession.GetCanonicalPath(path);
- // using SSH_FXP_REALPATH is not an alternative as the SFTP specification has not always
- // been clear on how the server should respond when the specified path is not present on
- // the server:
- //
- // SSH 1 to 4:
- // No mention of how the server should respond if the path is not present on the server.
- //
- // SSH 5:
- // The server SHOULD fail the request if the path is not present on the server.
- //
- // SSH 6:
- // Draft 06: The server SHOULD fail the request if the path is not present on the server.
- // Draft 07 to 13: The server MUST NOT fail the request if the path does not exist.
- //
- // Note that SSH 6 (draft 06 and forward) allows for more control options, but we
- // currently only support up to v3.
+ /*
+ * Using SSH_FXP_REALPATH is not an alternative as the SFTP specification has not always
+ * been clear on how the server should respond when the specified path is not present on
+ * the server:
+ *
+ * SSH 1 to 4:
+ * No mention of how the server should respond if the path is not present on the server.
+ *
+ * SSH 5:
+ * The server SHOULD fail the request if the path is not present on the server.
+ *
+ * SSH 6:
+ * Draft 06: The server SHOULD fail the request if the path is not present on the server.
+ * Draft 07 to 13: The server MUST NOT fail the request if the path does not exist.
+ *
+ * Note that SSH 6 (draft 06 and forward) allows for more control options, but we
+ * currently only support up to v3.
+ */
try
{
@@ -1233,12 +1236,12 @@ public async Task GetStatusAsync(string path, Cancella
///
/// The file to append the lines to. The file is created if it does not already exist.
/// The lines to append to the file.
- /// is -or- is .
+ /// is . -or- is .
/// Client is not connected.
/// The specified path is invalid, or its directory was not found on the remote host.
/// The method was called after the client was disposed.
///
- /// The characters are written to the file using UTF-8 encoding without a Byte-Order Mark (BOM)
+ /// The characters are written to the file using UTF-8 encoding without a byte-order mark (BOM).
///
public void AppendAllLines(string path, IEnumerable contents)
{
@@ -1710,10 +1713,13 @@ public string[] ReadAllLines(string path)
/// The method was called after the client was disposed.
public string[] ReadAllLines(string path, Encoding encoding)
{
- // we use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
- // for the SftpFileStream; may want to revisit this later
+ /*
+ * We use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
+ * for the SftpFileStream. We may want to revisit this later.
+ */
var lines = new List();
+
using (var stream = new StreamReader(OpenRead(path), encoding))
{
while (!stream.EndOfStream)
@@ -1721,6 +1727,7 @@ public string[] ReadAllLines(string path, Encoding encoding)
lines.Add(stream.ReadLine());
}
}
+
return lines.ToArray();
}
@@ -1752,8 +1759,10 @@ public string ReadAllText(string path)
/// The method was called after the client was disposed.
public string ReadAllText(string path, Encoding encoding)
{
- // we use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
- // for the SftpFileStream; may want to revisit this later
+ /*
+ * We use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
+ * for the SftpFileStream. We may want to revisit this later.
+ */
using (var stream = new StreamReader(OpenRead(path), encoding))
{
@@ -2074,15 +2083,6 @@ public void SetAttributes(string path, SftpFileAttributes fileAttributes)
_sftpSession.RequestSetStat(fullPath, fileAttributes);
}
- // Please don't forget this when you implement these methods: is .
- //public FileSecurity GetAccessControl(string path);
- //public FileSecurity GetAccessControl(string path, AccessControlSections includeSections);
- //public DateTime GetCreationTime(string path);
- //public DateTime GetCreationTimeUtc(string path);
- //public void SetAccessControl(string path, FileSecurity fileSecurity);
- //public void SetCreationTime(string path, DateTime creationTime);
- //public void SetCreationTimeUtc(string path, DateTime creationTimeUtc);
-
#endregion // File Methods
#region SynchronizeDirectories
@@ -2099,7 +2099,7 @@ public void SetAttributes(string path, SftpFileAttributes fileAttributes)
/// is .
/// is or contains only whitespace.
/// was not found on the remote host.
- /// If a problem occurs while copying the file
+ /// If a problem occurs while copying the file.
public IEnumerable SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern)
{
if (sourcePath is null)
@@ -2128,7 +2128,7 @@ public IEnumerable SynchronizeDirectories(string sourcePath, string de
///
/// is .
/// is or contains only whitespace.
- /// If a problem occurs while copying the file
+ /// If a problem occurs while copying the file.
public IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state)
{
if (sourcePath is null)
@@ -2180,7 +2180,7 @@ public IEnumerable EndSynchronizeDirectories(IAsyncResult asyncResult)
return ar.EndInvoke();
}
- private IEnumerable InternalSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, SftpSynchronizeDirectoriesAsyncResult asynchResult)
+ private List InternalSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, SftpSynchronizeDirectoriesAsyncResult asynchResult)
{
if (!Directory.Exists(sourcePath))
{
@@ -2228,8 +2228,7 @@ private IEnumerable InternalSynchronizeDirectories(string sourcePath,
var isDifferent = true;
if (destDict.TryGetValue(localFile.Name, out var remoteFile))
{
- // TODO: Use md5 to detect a difference
- //ltang: File exists at the destination => Using filesize to detect the difference
+ // File exists at the destination, use filesize to detect if there's a difference
isDifferent = localFile.Length != remoteFile.Length;
}
@@ -2238,7 +2237,9 @@ private IEnumerable InternalSynchronizeDirectories(string sourcePath,
var remoteFileName = string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", destinationPath, localFile.Name);
try
{
+#pragma warning disable CA2000 // Dispose objects before losing scope; false positive
using (var file = File.OpenRead(localFile.FullName))
+#pragma warning restore CA2000 // Dispose objects before losing scope; false positive
{
InternalUploadFile(file, remoteFileName, uploadFlag, asyncResult: null, uploadCallback: null);
}
@@ -2273,7 +2274,7 @@ private IEnumerable InternalSynchronizeDirectories(string sourcePath,
///
/// is .
/// Client not connected.
- private IEnumerable InternalListDirectory(string path, Action listCallback)
+ private List InternalListDirectory(string path, Action listCallback)
{
if (path is null)
{
@@ -2291,7 +2292,11 @@ private IEnumerable InternalListDirectory(string path, Action li
var basePath = fullPath;
- if (!basePath.EndsWith("/"))
+#if NET || NETSTANDARD2_1_OR_GREATER
+ if (!basePath.EndsWith('/'))
+#else
+ if (!basePath.EndsWith("/", StringComparison.Ordinal))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
{
basePath = string.Format("{0}/", fullPath);
}
@@ -2309,10 +2314,10 @@ private IEnumerable InternalListDirectory(string path, Action li
f.Value));
}
- // Call callback to report number of files read
+ // Call callback to report number of files read
if (listCallback is not null)
{
- // Execute callback on different thread
+ // Execute callback on different thread
ThreadAbstraction.ExecuteThread(() => listCallback(result.Count));
}
@@ -2377,10 +2382,10 @@ private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncR
if (downloadCallback is not null)
{
- // copy offset to ensure it's not modified between now and execution of callback
+ // Copy offset to ensure it's not modified between now and execution of callback
var downloadOffset = totalBytesRead;
- // Execute callback on different thread
+ // Execute callback on different thread
ThreadAbstraction.ExecuteThread(() => { downloadCallback(downloadOffset); });
}
}
@@ -2447,10 +2452,10 @@ private void InternalUploadFile(Stream input, string path, Flags flags, SftpUplo
_ = Interlocked.Decrement(ref expectedResponses);
_ = responseReceivedWaitHandle.Set();
- // Call callback to report number of bytes written
+ // Call callback to report number of bytes written
if (uploadCallback is not null)
{
- // Execute callback on different thread
+ // Execute callback on different thread
ThreadAbstraction.ExecuteThread(() => uploadCallback(writtenBytes));
}
}
@@ -2464,13 +2469,14 @@ private void InternalUploadFile(Stream input, string path, Flags flags, SftpUplo
}
else if (expectedResponses > 0)
{
- // Wait for expectedResponses to change
+ // Wait for expectedResponses to change
_sftpSession.WaitOnHandle(responseReceivedWaitHandle, _operationTimeout);
}
}
while (expectedResponses > 0 || bytesRead > 0);
_sftpSession.RequestClose(handle);
+ responseReceivedWaitHandle.Dispose();
}
///
@@ -2501,7 +2507,7 @@ protected override void OnDisconnecting()
}
///
- /// Releases unmanaged and - optionally - managed resources
+ /// Releases unmanaged and - optionally - managed resources.
///
/// to release both managed and unmanaged resources; to release only unmanaged resources.
protected override void Dispose(bool disposing)
diff --git a/src/Renci.SshNet/Shell.cs b/src/Renci.SshNet/Shell.cs
index 0f80c3ca7..4a73e58fb 100644
--- a/src/Renci.SshNet/Shell.cs
+++ b/src/Renci.SshNet/Shell.cs
@@ -24,9 +24,9 @@ public class Shell : IDisposable
private readonly Stream _outputStream;
private readonly Stream _extendedOutputStream;
private readonly int _bufferSize;
- private EventWaitHandle _dataReaderTaskCompleted;
+ private ManualResetEvent _dataReaderTaskCompleted;
private IChannelSession _channel;
- private EventWaitHandle _channelClosedWaitHandle;
+ private AutoResetEvent _channelClosedWaitHandle;
private Stream _input;
///
@@ -130,7 +130,7 @@ public void Start()
var readTask = _input.ReadAsync(buffer, 0, buffer.Length);
var readWaitHandle = ((IAsyncResult) readTask).AsyncWaitHandle;
- if (WaitHandle.WaitAny(new[] {readWaitHandle, _channelClosedWaitHandle}) == 0)
+ if (WaitHandle.WaitAny(new[] { readWaitHandle, _channelClosedWaitHandle }) == 0)
{
var read = readTask.GetAwaiter().GetResult();
_channel.SendData(buffer, 0, read);
diff --git a/src/Renci.SshNet/ShellStream.cs b/src/Renci.SshNet/ShellStream.cs
index 80097bafe..ce57072d5 100644
--- a/src/Renci.SshNet/ShellStream.cs
+++ b/src/Renci.SshNet/ShellStream.cs
@@ -155,10 +155,14 @@ public override bool CanWrite
/// Methods were called after the stream was closed.
public override void Flush()
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_channel is null, this);
+#else
if (_channel is null)
{
- throw new ObjectDisposedException("ShellStream");
+ throw new ObjectDisposedException(GetType().FullName);
}
+#endif // NET7_0_OR_GREATER
if (_outgoing.Count > 0)
{
@@ -199,36 +203,6 @@ public override long Position
set { throw new NotSupportedException(); }
}
- ///
- /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
- ///
- /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source.
- /// The zero-based byte offset in at which to begin storing the data read from the current stream.
- /// The maximum number of bytes to be read from the current stream.
- ///
- /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
- ///
- /// The sum of and is larger than the buffer length.
- /// is .
- /// or is negative.
- /// An I/O error occurs.
- /// The stream does not support reading.
- /// Methods were called after the stream was closed.
- public override int Read(byte[] buffer, int offset, int count)
- {
- var i = 0;
-
- lock (_incoming)
- {
- for (; i < count && _incoming.Count > 0; i++)
- {
- buffer[offset + i] = _incoming.Dequeue();
- }
- }
-
- return i;
- }
-
///
/// This method is not supported.
///
@@ -257,31 +231,6 @@ public override void SetLength(long value)
throw new NotSupportedException();
}
- ///
- /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
- ///
- /// An array of bytes. This method copies bytes from to the current stream.
- /// The zero-based byte offset in at which to begin copying bytes to the current stream.
- /// The number of bytes to be written to the current stream.
- /// The sum of and is greater than the buffer length.
- /// is .
- /// or is negative.
- /// An I/O error occurs.
- /// The stream does not support writing.
- /// Methods were called after the stream was closed.
- public override void Write(byte[] buffer, int offset, int count)
- {
- foreach (var b in buffer.Take(offset, count))
- {
- if (_outgoing.Count == _bufferSize)
- {
- Flush();
- }
-
- _outgoing.Enqueue(b);
- }
- }
-
///
/// Expects the specified expression and performs action when one is found.
///
@@ -351,6 +300,95 @@ public void Expect(TimeSpan timeout, params ExpectAction[] expectActions)
while (!expectedFound);
}
+ ///
+ /// Expects the expression specified by text.
+ ///
+ /// The text to expect.
+ ///
+ /// Text available in the shell that ends with expected text.
+ ///
+ public string Expect(string text)
+ {
+ return Expect(new Regex(Regex.Escape(text)), Session.InfiniteTimeSpan);
+ }
+
+ ///
+ /// Expects the expression specified by text.
+ ///
+ /// The text to expect.
+ /// Time to wait for input.
+ ///
+ /// The text available in the shell that ends with expected text, or if the specified time has elapsed.
+ ///
+ public string Expect(string text, TimeSpan timeout)
+ {
+ return Expect(new Regex(Regex.Escape(text)), timeout);
+ }
+
+ ///
+ /// Expects the expression specified by regular expression.
+ ///
+ /// The regular expression to expect.
+ ///
+ /// The text available in the shell that contains all the text that ends with expected expression.
+ ///
+ public string Expect(Regex regex)
+ {
+ return Expect(regex, TimeSpan.Zero);
+ }
+
+ ///
+ /// Expects the expression specified by regular expression.
+ ///
+ /// The regular expression to expect.
+ /// Time to wait for input.
+ ///
+ /// The text available in the shell that contains all the text that ends with expected expression,
+ /// or if the specified time has elapsed.
+ ///
+ public string Expect(Regex regex, TimeSpan timeout)
+ {
+ var text = string.Empty;
+
+ while (true)
+ {
+ lock (_incoming)
+ {
+ if (_incoming.Count > 0)
+ {
+ text = _encoding.GetString(_incoming.ToArray(), 0, _incoming.Count);
+ }
+
+ var match = regex.Match(text);
+
+ if (match.Success)
+ {
+ // Remove processed items from the queue
+ for (var i = 0; i < match.Index + match.Length && _incoming.Count > 0; i++)
+ {
+ _ = _incoming.Dequeue();
+ }
+
+ break;
+ }
+ }
+
+ if (timeout.Ticks > 0)
+ {
+ if (!_dataReceived.WaitOne(timeout))
+ {
+ return null;
+ }
+ }
+ else
+ {
+ _ = _dataReceived.WaitOne();
+ }
+ }
+
+ return text;
+ }
+
///
/// Begins the expect.
///
@@ -400,7 +438,9 @@ public IAsyncResult BeginExpect(AsyncCallback callback, object state, params Exp
///
/// An that references the asynchronous operation.
///
+#pragma warning disable CA1859 // Use concrete types when possible for improved performance
public IAsyncResult BeginExpect(TimeSpan timeout, AsyncCallback callback, object state, params ExpectAction[] expectActions)
+#pragma warning restore CA1859 // Use concrete types when possible for improved performance
{
var text = string.Empty;
@@ -481,6 +521,9 @@ public IAsyncResult BeginExpect(TimeSpan timeout, AsyncCallback callback, object
/// Ends the execute.
///
/// The async result.
+ ///
+ /// Text available in the shell that ends with expected text.
+ ///
/// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.
public string EndExpect(IAsyncResult asyncResult)
{
@@ -493,95 +536,6 @@ public string EndExpect(IAsyncResult asyncResult)
return ar.EndInvoke();
}
- ///
- /// Expects the expression specified by text.
- ///
- /// The text to expect.
- ///
- /// Text available in the shell that ends with expected text.
- ///
- public string Expect(string text)
- {
- return Expect(new Regex(Regex.Escape(text)), Session.InfiniteTimeSpan);
- }
-
- ///
- /// Expects the expression specified by text.
- ///
- /// The text to expect.
- /// Time to wait for input.
- ///
- /// The text available in the shell that ends with expected text, or if the specified time has elapsed.
- ///
- public string Expect(string text, TimeSpan timeout)
- {
- return Expect(new Regex(Regex.Escape(text)), timeout);
- }
-
- ///
- /// Expects the expression specified by regular expression.
- ///
- /// The regular expression to expect.
- ///
- /// The text available in the shell that contains all the text that ends with expected expression.
- ///
- public string Expect(Regex regex)
- {
- return Expect(regex, TimeSpan.Zero);
- }
-
- ///
- /// Expects the expression specified by regular expression.
- ///
- /// The regular expression to expect.
- /// Time to wait for input.
- ///
- /// The text available in the shell that contains all the text that ends with expected expression,
- /// or if the specified time has elapsed.
- ///
- public string Expect(Regex regex, TimeSpan timeout)
- {
- var text = string.Empty;
-
- while (true)
- {
- lock (_incoming)
- {
- if (_incoming.Count > 0)
- {
- text = _encoding.GetString(_incoming.ToArray(), 0, _incoming.Count);
- }
-
- var match = regex.Match(text);
-
- if (match.Success)
- {
- // Remove processed items from the queue
- for (var i = 0; i < match.Index + match.Length && _incoming.Count > 0; i++)
- {
- _ = _incoming.Dequeue();
- }
-
- break;
- }
- }
-
- if (timeout.Ticks > 0)
- {
- if (!_dataReceived.WaitOne(timeout))
- {
- return null;
- }
- }
- else
- {
- _ = _dataReceived.WaitOne();
- }
- }
-
- return text;
- }
-
///
/// Reads the line from the shell. If line is not available it will block the execution and will wait for new line.
///
@@ -667,6 +621,36 @@ public string Read()
return text;
}
+ ///
+ /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
+ ///
+ /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source.
+ /// The zero-based byte offset in at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ ///
+ /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
+ ///
+ /// The sum of and is larger than the buffer length.
+ /// is .
+ /// or is negative.
+ /// An I/O error occurs.
+ /// The stream does not support reading.
+ /// Methods were called after the stream was closed.
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ var i = 0;
+
+ lock (_incoming)
+ {
+ for (; i < count && _incoming.Count > 0; i++)
+ {
+ buffer[offset + i] = _incoming.Dequeue();
+ }
+ }
+
+ return i;
+ }
+
///
/// Writes the specified text to the shell.
///
@@ -681,15 +665,44 @@ public void Write(string text)
return;
}
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_channel is null, this);
+#else
if (_channel is null)
{
- throw new ObjectDisposedException("ShellStream");
+ throw new ObjectDisposedException(GetType().FullName);
}
+#endif // NET7_0_OR_GREATER
var data = _encoding.GetBytes(text);
_channel.SendData(data);
}
+ ///
+ /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
+ ///
+ /// An array of bytes. This method copies bytes from to the current stream.
+ /// The zero-based byte offset in at which to begin copying bytes to the current stream.
+ /// The number of bytes to be written to the current stream.
+ /// The sum of and is greater than the buffer length.
+ /// is .
+ /// or is negative.
+ /// An I/O error occurs.
+ /// The stream does not support writing.
+ /// Methods were called after the stream was closed.
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ foreach (var b in buffer.Take(offset, count))
+ {
+ if (_outgoing.Count == _bufferSize)
+ {
+ Flush();
+ }
+
+ _outgoing.Enqueue(b);
+ }
+ }
+
///
/// Writes the line to the shell.
///
diff --git a/src/Renci.SshNet/SshClient.cs b/src/Renci.SshNet/SshClient.cs
index 171b79775..753ab8f9f 100644
--- a/src/Renci.SshNet/SshClient.cs
+++ b/src/Renci.SshNet/SshClient.cs
@@ -27,7 +27,7 @@ public class SshClient : BaseClient
///
private bool _isDisposed;
- private Stream _inputStream;
+ private MemoryStream _inputStream;
///
/// Gets the list of forwarded ports.
@@ -68,7 +68,9 @@ public SshClient(ConnectionInfo connectionInfo)
/// is not within and .
[SuppressMessage("Microsoft.Reliability", "C2A000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
public SshClient(string host, int port, string username, string password)
+#pragma warning disable CA2000 // Dispose objects before losing scope
: this(new PasswordConnectionInfo(host, port, username, password), ownsConnectionInfo: true)
+#pragma warning restore CA2000 // Dispose objects before losing scope
{
}
@@ -359,12 +361,18 @@ public Shell CreateShell(Stream input, Stream output, Stream extendedOutput)
/// Client is not connected.
public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize)
{
- // TODO let shell dispose of input stream when we own the stream!
+ /*
+ * TODO Issue #1224: let shell dispose of input stream when we own the stream!
+ */
_inputStream = new MemoryStream();
- var writer = new StreamWriter(_inputStream, encoding);
- writer.Write(input);
- writer.Flush();
+
+ using (var writer = new StreamWriter(_inputStream, encoding, bufferSize: 1024, leaveOpen: true))
+ {
+ writer.Write(input);
+ writer.Flush();
+ }
+
_ = _inputStream.Seek(0, SeekOrigin.Begin);
return CreateShell(_inputStream, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize);
diff --git a/src/Renci.SshNet/SshCommand.cs b/src/Renci.SshNet/SshCommand.cs
index 9bcd30342..5b93354f7 100644
--- a/src/Renci.SshNet/SshCommand.cs
+++ b/src/Renci.SshNet/SshCommand.cs
@@ -62,7 +62,9 @@ public class SshCommand : IDisposable
///
///
///
+#pragma warning disable CA1859 // Use concrete types when possible for improved performance
public Stream OutputStream { get; private set; }
+#pragma warning restore CA1859 // Use concrete types when possible for improved performance
///
/// Gets the extended output stream.
@@ -70,7 +72,9 @@ public class SshCommand : IDisposable
///
///
///
+#pragma warning disable CA1859 // Use concrete types when possible for improved performance
public Stream ExtendedOutputStream { get; private set; }
+#pragma warning restore CA1859 // Use concrete types when possible for improved performance
///
/// Gets the command execution result.
@@ -86,9 +90,14 @@ public string Result
if (OutputStream != null && OutputStream.Length > 0)
{
- // do not dispose the StreamReader, as it would also dispose the stream
- var sr = new StreamReader(OutputStream, _encoding);
- _ = _result.Append(sr.ReadToEnd());
+ using (var sr = new StreamReader(OutputStream,
+ _encoding,
+ detectEncodingFromByteOrderMarks: true,
+ bufferSize: 1024,
+ leaveOpen: true))
+ {
+ _ = _result.Append(sr.ReadToEnd());
+ }
}
return _result.ToString();
@@ -111,9 +120,14 @@ public string Error
if (ExtendedOutputStream != null && ExtendedOutputStream.Length > 0)
{
- // do not dispose the StreamReader, as it would also dispose the stream
- var sr = new StreamReader(ExtendedOutputStream, _encoding);
- _ = _error.Append(sr.ReadToEnd());
+ using (var sr = new StreamReader(ExtendedOutputStream,
+ _encoding,
+ detectEncodingFromByteOrderMarks: true,
+ bufferSize: 1024,
+ leaveOpen: true))
+ {
+ _ = _error.Append(sr.ReadToEnd());
+ }
}
return _error.ToString();
@@ -206,7 +220,9 @@ public IAsyncResult BeginExecute(AsyncCallback callback)
/// CommandText property is empty.
/// Client is not connected.
/// Operation has timed out.
+#pragma warning disable CA1859 // Use concrete types when possible for improved performance
public IAsyncResult BeginExecute(AsyncCallback callback, object state)
+#pragma warning restore CA1859 // Use concrete types when possible for improved performance
{
// Prevent from executing BeginExecute before calling EndExecute
if (_asyncResult != null && !_asyncResult.EndCalled)
@@ -321,10 +337,24 @@ public string EndExecute(IAsyncResult asyncResult)
}
}
+ ///
+ /// Cancels command execution in asynchronous scenarios.
+ ///
+ public void CancelAsync()
+ {
+ if (_channel is not null && _channel.IsOpen && _asyncResult is not null)
+ {
+ // TODO: check with Oleg if we shouldn't dispose the channel and uninitialize it ?
+ _channel.Dispose();
+ }
+ }
+
///
/// Executes command specified by property.
///
- /// Command execution result
+ ///
+ /// Command execution result.
+ ///
///
///
///
@@ -337,18 +367,6 @@ public string Execute()
return EndExecute(BeginExecute(callback: null, state: null));
}
- ///
- /// Cancels command execution in asynchronous scenarios.
- ///
- public void CancelAsync()
- {
- if (_channel is not null && _channel.IsOpen && _asyncResult is not null)
- {
- // TODO: check with Oleg if we shouldn't dispose the channel and uninitialize it ?
- _channel.Dispose();
- }
- }
-
///
/// Executes the specified command text.
///
@@ -493,7 +511,6 @@ private void WaitOnHandle(WaitHandle waitHandle)
throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Command '{0}' has timed out.", CommandText));
default:
throw new SshException($"Unexpected element '{signaledElement.ToString(CultureInfo.InvariantCulture)}' signaled.");
-
}
}
diff --git a/src/Renci.SshNet/SshMessageFactory.cs b/src/Renci.SshNet/SshMessageFactory.cs
index 5941c120e..efa861256 100644
--- a/src/Renci.SshNet/SshMessageFactory.cs
+++ b/src/Renci.SshNet/SshMessageFactory.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+
using Renci.SshNet.Common;
using Renci.SshNet.Messages;
using Renci.SshNet.Messages.Authentication;
@@ -13,9 +14,44 @@ internal sealed class SshMessageFactory
{
private readonly MessageMetadata[] _enabledMessagesByNumber;
private readonly bool[] _activatedMessagesById;
+ private readonly object _lock = new object();
- internal static readonly MessageMetadata[] AllMessages;
- private static readonly Dictionary MessagesByName;
+ private static readonly MessageMetadata[] AllMessages = new MessageMetadata[]
+ {
+ new MessageMetadata(0, "SSH_MSG_KEXINIT", 20),
+ new MessageMetadata(1, "SSH_MSG_NEWKEYS", 21),
+ new MessageMetadata(2, "SSH_MSG_REQUEST_FAILURE", 82),
+ new MessageMetadata(3, "SSH_MSG_CHANNEL_OPEN_FAILURE", 92),
+ new MessageMetadata(4, "SSH_MSG_CHANNEL_FAILURE", 100),
+ new MessageMetadata(5, "SSH_MSG_CHANNEL_EXTENDED_DATA", 95),
+ new MessageMetadata(6, "SSH_MSG_CHANNEL_DATA", 94),
+ new MessageMetadata(7, "SSH_MSG_CHANNEL_REQUEST", 98),
+ new MessageMetadata(8, "SSH_MSG_USERAUTH_BANNER", 53),
+ new MessageMetadata(9, "SSH_MSG_USERAUTH_INFO_RESPONSE", 61),
+ new MessageMetadata(10, "SSH_MSG_USERAUTH_FAILURE", 51),
+ new MessageMetadata(11, "SSH_MSG_DEBUG", 4),
+ new MessageMetadata(12, "SSH_MSG_GLOBAL_REQUEST", 80),
+ new MessageMetadata(13, "SSH_MSG_CHANNEL_OPEN", 90),
+ new MessageMetadata(14, "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", 91),
+ new MessageMetadata(15, "SSH_MSG_USERAUTH_INFO_REQUEST", 60),
+ new MessageMetadata(16, "SSH_MSG_UNIMPLEMENTED", 3),
+ new MessageMetadata(17, "SSH_MSG_REQUEST_SUCCESS", 81),
+ new MessageMetadata(18, "SSH_MSG_CHANNEL_SUCCESS", 99),
+ new MessageMetadata(19, "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", 60),
+ new MessageMetadata(20, "SSH_MSG_DISCONNECT", 1),
+ new MessageMetadata(21, "SSH_MSG_USERAUTH_SUCCESS", 52),
+ new MessageMetadata(22, "SSH_MSG_USERAUTH_PK_OK", 60),
+ new MessageMetadata(23, "SSH_MSG_IGNORE", 2),
+ new MessageMetadata(24, "SSH_MSG_CHANNEL_WINDOW_ADJUST", 93),
+ new MessageMetadata(25, "SSH_MSG_CHANNEL_EOF", 96),
+ new MessageMetadata(26, "SSH_MSG_CHANNEL_CLOSE", 97),
+ new MessageMetadata(27, "SSH_MSG_SERVICE_ACCEPT", 6),
+ new MessageMetadata(28, "SSH_MSG_KEX_DH_GEX_GROUP", 31),
+ new MessageMetadata(29, "SSH_MSG_KEXDH_REPLY", 31),
+ new MessageMetadata(30, "SSH_MSG_KEX_DH_GEX_REPLY", 33),
+ new MessageMetadata(31, "SSH_MSG_KEX_ECDH_REPLY", 31)
+ };
+ private static readonly Dictionary MessagesByName = CreateMessagesByNameMapping();
///
/// Defines the highest message number that is currently supported.
@@ -27,52 +63,6 @@ internal sealed class SshMessageFactory
///
internal const int TotalMessageCount = 32;
- static SshMessageFactory()
- {
- AllMessages = new MessageMetadata[]
- {
- new MessageMetadata(0, "SSH_MSG_KEXINIT", 20),
- new MessageMetadata(1, "SSH_MSG_NEWKEYS", 21),
- new MessageMetadata(2, "SSH_MSG_REQUEST_FAILURE", 82),
- new MessageMetadata(3, "SSH_MSG_CHANNEL_OPEN_FAILURE", 92),
- new MessageMetadata(4, "SSH_MSG_CHANNEL_FAILURE", 100),
- new MessageMetadata(5, "SSH_MSG_CHANNEL_EXTENDED_DATA", 95),
- new MessageMetadata(6, "SSH_MSG_CHANNEL_DATA", 94),
- new MessageMetadata(7, "SSH_MSG_CHANNEL_REQUEST", 98),
- new MessageMetadata(8, "SSH_MSG_USERAUTH_BANNER", 53),
- new MessageMetadata(9, "SSH_MSG_USERAUTH_INFO_RESPONSE", 61),
- new MessageMetadata(10, "SSH_MSG_USERAUTH_FAILURE", 51),
- new MessageMetadata(11, "SSH_MSG_DEBUG", 4),
- new MessageMetadata(12, "SSH_MSG_GLOBAL_REQUEST", 80),
- new MessageMetadata(13, "SSH_MSG_CHANNEL_OPEN", 90),
- new MessageMetadata(14, "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", 91),
- new MessageMetadata(15, "SSH_MSG_USERAUTH_INFO_REQUEST", 60),
- new MessageMetadata(16, "SSH_MSG_UNIMPLEMENTED", 3),
- new MessageMetadata(17, "SSH_MSG_REQUEST_SUCCESS", 81),
- new MessageMetadata(18, "SSH_MSG_CHANNEL_SUCCESS", 99),
- new MessageMetadata(19, "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", 60),
- new MessageMetadata(20, "SSH_MSG_DISCONNECT", 1),
- new MessageMetadata(21, "SSH_MSG_USERAUTH_SUCCESS", 52),
- new MessageMetadata(22, "SSH_MSG_USERAUTH_PK_OK", 60),
- new MessageMetadata(23, "SSH_MSG_IGNORE", 2),
- new MessageMetadata(24, "SSH_MSG_CHANNEL_WINDOW_ADJUST", 93),
- new MessageMetadata(25, "SSH_MSG_CHANNEL_EOF", 96),
- new MessageMetadata(26, "SSH_MSG_CHANNEL_CLOSE", 97),
- new MessageMetadata(27, "SSH_MSG_SERVICE_ACCEPT", 6),
- new MessageMetadata(28, "SSH_MSG_KEX_DH_GEX_GROUP", 31),
- new MessageMetadata(29, "SSH_MSG_KEXDH_REPLY", 31),
- new MessageMetadata(30, "SSH_MSG_KEX_DH_GEX_REPLY", 33),
- new MessageMetadata(31, "SSH_MSG_KEX_ECDH_REPLY", 31)
- };
-
- MessagesByName = new Dictionary(AllMessages.Length);
- for (var i = 0; i < AllMessages.Length; i++)
- {
- var messageMetadata = AllMessages[i];
- MessagesByName.Add(messageMetadata.Name, messageMetadata);
- }
- }
-
///
/// Initializes a new instance of the class.
///
@@ -169,7 +159,7 @@ public void EnableAndActivateMessage(string messageName)
throw new ArgumentNullException(nameof(messageName));
}
- lock (this)
+ lock (_lock)
{
if (!MessagesByName.TryGetValue(messageName, out var messageMetadata))
{
@@ -196,7 +186,7 @@ public void DisableAndDeactivateMessage(string messageName)
throw new ArgumentNullException(nameof(messageName));
}
- lock (this)
+ lock (_lock)
{
if (!MessagesByName.TryGetValue(messageName, out var messageMetadata))
{
@@ -216,6 +206,19 @@ public void DisableAndDeactivateMessage(string messageName)
}
}
+ private static Dictionary CreateMessagesByNameMapping()
+ {
+ var messagesByName = new Dictionary(AllMessages.Length);
+
+ for (var i = 0; i < AllMessages.Length; i++)
+ {
+ var messageMetadata = AllMessages[i];
+ messagesByName.Add(messageMetadata.Name, messageMetadata);
+ }
+
+ return messagesByName;
+ }
+
private static SshException CreateMessageTypeNotSupportedException(byte messageNumber)
{
throw new SshException(string.Format(CultureInfo.InvariantCulture, "Message type {0} is not supported.", messageNumber));
@@ -229,11 +232,13 @@ private static SshException CreateMessageNotSupportedException(string messageNam
private static SshException CreateMessageTypeAlreadyEnabledForOtherMessageException(byte messageNumber, string messageName, string currentEnabledForMessageName)
{
throw new SshException(string.Format(CultureInfo.InvariantCulture,
- "Cannot enable message '{0}'. Message type {1} is already enabled for '{2}'.",
- messageName, messageNumber, currentEnabledForMessageName));
+ "Cannot enable message '{0}'. Message type {1} is already enabled for '{2}'.",
+ messageName,
+ messageNumber,
+ currentEnabledForMessageName));
}
- internal abstract class MessageMetadata
+ private abstract class MessageMetadata
{
protected MessageMetadata(byte id, string name, byte number)
{
@@ -242,16 +247,16 @@ protected MessageMetadata(byte id, string name, byte number)
Number = number;
}
- public readonly byte Id;
+ public byte Id { get; }
- public readonly string Name;
+ public string Name { get; }
- public readonly byte Number;
+ public byte Number { get; }
public abstract Message Create();
}
- internal sealed class MessageMetadata : MessageMetadata
+ private sealed class MessageMetadata : MessageMetadata
where T : Message, new()
{
public MessageMetadata(byte id, string name, byte number)
diff --git a/src/Renci.SshNet/SubsystemSession.cs b/src/Renci.SshNet/SubsystemSession.cs
index ab6182bba..70385d903 100644
--- a/src/Renci.SshNet/SubsystemSession.cs
+++ b/src/Renci.SshNet/SubsystemSession.cs
@@ -307,12 +307,12 @@ public bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout)
/// Blocks the current thread until the specified gets signaled, using a
/// 32-bit signed integer to specify the time interval in milliseconds.
///
- /// The first handle to wait for.
- /// The second handle to wait for.
+ /// The first handle to wait for.
+ /// The second handle to wait for.
/// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
///
- /// 0 if received a signal within the specified timeout, and 1
- /// if received a signal within the specified timeout.
+ /// 0 if received a signal within the specified timeout, and 1
+ /// if received a signal within the specified timeout.
///
/// The connection was closed by the server.
/// The channel was closed.
@@ -324,19 +324,19 @@ public bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout)
/// or session event.
///
///
- /// When both and are signaled during the call,
+ /// When both and are signaled during the call,
/// then 0 is returned.
///
///
- public int WaitAny(WaitHandle waitHandle1, WaitHandle waitHandle2, int millisecondsTimeout)
+ public int WaitAny(WaitHandle waitHandleA, WaitHandle waitHandleB, int millisecondsTimeout)
{
var waitHandles = new[]
{
_errorOccuredWaitHandle,
_sessionDisconnectedWaitHandle,
_channelClosedWaitHandle,
- waitHandle1,
- waitHandle2
+ waitHandleA,
+ waitHandleB
};
var result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout);
@@ -547,10 +547,14 @@ protected virtual void Dispose(bool disposing)
private void EnsureNotDisposed()
{
+#if NET7_0_OR_GREATER
+ ObjectDisposedException.ThrowIf(_isDisposed, this);
+#else
if (_isDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
+#endif // NET7_0_OR_GREATER
}
}
}
diff --git a/test/.editorconfig b/test/.editorconfig
index ff0eebcf3..60515fd14 100644
--- a/test/.editorconfig
+++ b/test/.editorconfig
@@ -1,6 +1,10 @@
[*.cs]
-# Sonar rules
+#### Sonar rules ####
+
+# S1215: ""GC.Collect" should not be called
+# https://rules.sonarsource.com/csharp/RSPEC-1215
+dotnet_diagnostic.S1215.severity = none
# S1854: Unused assignments should be removed
# https://rules.sonarsource.com/csharp/RSPEC-1854
@@ -18,6 +22,12 @@
# For unit tests, we do not care about this diagnostic.
dotnet_diagnostic.S1854.severity = none
+#### Meziantou.Analyzer rules ####
+
+# MA0089: Optimize string method usage
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0089.md
+dotnet_diagnostic.MA0089.severity = suggestion
+
#### StyleCop rules ####
# SA1202: Elements must be ordered by access
@@ -95,6 +105,18 @@ dotnet_diagnostic.CA1001.severity = none
# We frequently use underscores in test classes and test methods.
dotnet_diagnostic.CA1707.severity = none
+# CA1824: Mark assemblies with NeutralResourcesLanguageAttribute
+# https://learn.microsoft.com/en-US/dotnet/fundamentals/code-analysis/quality-rules/ca1824
+#
+# We do not care (much) about performance for tests.
+dotnet_diagnostic.CA1824.severity = none
+
+# CA1835: Prefer the memory-based overloads of ReadAsync/WriteAsync methods in stream-based classes
+# https://learn.microsoft.com/en-US/dotnet/fundamentals/code-analysis/quality-rules/ca1835
+#
+# We do not care about this for unit tests.
+dotnet_diagnostic.CA1835.severity = none
+
# CA1711: Identifiers should not have incorrect suffix
#
# We frequently define test classes and test method with a suffix that refers to a type.
@@ -105,6 +127,11 @@ dotnet_diagnostic.CA1711.severity = none
# We do not care about this for unit tests.
dotnet_diagnostic.CA1720.severity = none
+# CA5351: Do not use broken cryptographic algorithms
+#
+# We do not care about this for unit tests.
+dotnet_diagnostic.CA5351.severity = none
+
# CA5394: Do not use insecure randomness
#
# We do not care about this for unit tests.
diff --git a/test/Renci.SshNet.Benchmarks/.editorconfig b/test/Renci.SshNet.Benchmarks/.editorconfig
new file mode 100644
index 000000000..e903a76d8
--- /dev/null
+++ b/test/Renci.SshNet.Benchmarks/.editorconfig
@@ -0,0 +1,72 @@
+[*.cs]
+
+#### Sonar rules ####
+
+# S125: Sections of code should not be commented out
+https://rules.sonarsource.com/csharp/RSPEC-125/
+dotnet_diagnostic.S125.severity = suggestion
+
+# S1118: Utility classes should not have public constructors
+# https://rules.sonarsource.com/csharp/RSPEC-1118/
+dotnet_diagnostic.S1118.severity = suggestion
+
+# S1450: Private fields only used as local variables in methods should become local variables
+# https://rules.sonarsource.com/csharp/RSPEC-1450/
+dotnet_diagnostic.S1450.severity = suggestion
+
+# S4144: Methods should not have identical implementations
+# https://rules.sonarsource.com/csharp/RSPEC-4144/
+dotnet_diagnostic.S4144.severity = suggestion
+
+#### SYSLIB diagnostics ####
+
+
+#### StyleCop Analyzers rules ####
+
+# SA1028: Code must not contain trailing whitespace
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1028.md
+dotnet_diagnostic.SA1028.severity = suggestion
+
+# SA1414: Tuple types in signatures should have element names
+https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1414.md
+dotnet_diagnostic.SA1414.severity = suggestion
+
+# SA1400: Access modifier must be declared
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1400.md
+dotnet_diagnostic.SA1400.severity = suggestion
+
+# SA1401: Fields must be private
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
+dotnet_diagnostic.SA1401.severity = suggestion
+
+# SA1411: Attribute constructor must not use unnecessary parenthesis
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1411.md
+dotnet_diagnostic.SA1411.severity = suggestion
+
+# SA1505: Opening braces must not be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1505.md
+dotnet_diagnostic.SA1505.severity = suggestion
+
+# SA1512: Single line comments must not be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1512.md
+dotnet_diagnostic.SA1512.severity = suggestion
+
+# SA1513: Closing brace must be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1513.md
+dotnet_diagnostic.SA1513.severity = suggestion
+
+#### Meziantou.Analyzer rules ####
+
+# MA0003: Add parameter name to improve readability
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0003.md
+dotnet_diagnostic.MA0003.severity = suggestion
+
+# MA0053: Make class sealed
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md
+dotnet_diagnostic.MA0053.severity = suggestion
+
+#### .NET Compiler Platform analysers rules ####
+
+# CA2000: Dispose objects before losing scope
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2000
+dotnet_diagnostic.CA2000.severity = suggestion
diff --git a/test/Renci.SshNet.IntegrationTests/.editorconfig b/test/Renci.SshNet.IntegrationTests/.editorconfig
index b94e29112..dfeca510a 100644
--- a/test/Renci.SshNet.IntegrationTests/.editorconfig
+++ b/test/Renci.SshNet.IntegrationTests/.editorconfig
@@ -1,5 +1,59 @@
[*.cs]
+#### Sonar rules ####
+
+# S108: Nested blocks of code should not be left empty
+# https://rules.sonarsource.com/csharp/RSPEC-108/
+dotnet_diagnostic.S108.severity = none
+
+# S125: Sections of code should not be commented out
+# https://rules.sonarsource.com/csharp/RSPEC-125/
+dotnet_diagnostic.S125.severity = none
+
+# S1118: Utility classes should not have public constructors
+# https://rules.sonarsource.com/csharp/RSPEC-1118/
+dotnet_diagnostic.S1118.severity = none
+
+# S1155: "Any()" should be used to test for emptiness
+# https://rules.sonarsource.com/csharp/RSPEC-1155/
+dotnet_diagnostic.S1155.severity = none
+
+# S1607: Tests should not be ignored
+# https://rules.sonarsource.com/csharp/RSPEC-1607/
+dotnet_diagnostic.S1607.severity = none
+
+# S2925: "Thread.Sleep" should not be used in tests
+# https://rules.sonarsource.com/csharp/RSPEC-2925/
+dotnet_diagnostic.S2925.severity = none
+
+# 53241: Methods should not return values that are never used
+# https://rules.sonarsource.com/csharp/RSPEC-3241/
+dotnet_diagnostic.S3241.severity = none
+
+# S3415: Assertion arguments should be passed in the correct order
+# https://rules.sonarsource.com/csharp/RSPEC-3415/
+dotnet_diagnostic.S3415.severity = none
+
+# S3881: "IDisposable" should be implemented correctly
+# https://rules.sonarsource.com/csharp/RSPEC-3881/
+dotnet_diagnostic.S3881.severity = none
+
+# S3966: Objects should not be disposed more than once
+# https://rules.sonarsource.com/csharp/RSPEC-3966/
+dotnet_diagnostic.S3966.severity = none
+
+# S4144: Methods should not have identical implementations
+# https://rules.sonarsource.com/csharp/RSPEC-4144/
+dotnet_diagnostic.S4144.severity = none
+
+# S6602: "Find" method should be used instead of the "FirstOrDefault" extension
+# https://rules.sonarsource.com/csharp/RSPEC-6602/
+dotnet_diagnostic.S6602.severity = none
+
+# S6610: "StartsWith" and "EndsWith" overloads that take a "char" should be used instead of the ones that take a "string"
+# https://rules.sonarsource.com/csharp/RSPEC-6610/
+dotnet_diagnostic.S6610.severity = none
+
#### SYSLIB diagnostics ####
# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time
@@ -9,8 +63,266 @@ dotnet_diagnostic.SYSLIB1045.severity = none
### StyleCop Analyzers rules ###
+# SA1000: Keywords must be spaced correctly
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1000.md
+dotnet_diagnostic.SA1000.severity = suggestion
+
+# SA1004: Documentation lines must begin with single space
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1004.md
+dotnet_diagnostic.SA1004.severity = suggestion
+
+# SA1005: Single line comments must begin with single space
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1005.md
+dotnet_diagnostic.SA1005.severity = suggestion
+
+# SA1012: Opening braces must be spaced correctly
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1012.md
+dotnet_diagnostic.SA1012.severity = suggestion
+
+# SA1025: Code must not contain multiple whitespace in a row
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1025.md
+dotnet_diagnostic.SA1025.severity = suggestion
+
+# SA1028: Code must not contain trailing whitespace
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1028.md
+dotnet_diagnostic.SA1028.severity = suggestion
+
+# SA1111: Closing parenthesis must be on line of last parameter
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1111.md
+dotnet_diagnostic.SA1111.severity = suggestion
+
+# SA1117: Parameters must be on same line or separate lines
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1117.md
+dotnet_diagnostic.SA1117.severity = suggestion
+
+# SA1119: Statement must not use unnecessary parenthesis
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1119.md
+dotnet_diagnostic.SA1119.severity = suggestion
+
+# SA1122: Use String.Empty for empty strings
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1122.md
+dotnet_diagnostic.SA1122.severity = suggestion
+
+# SA1123:Do not place regions within elements
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1123.md
+dotnet_diagnostic.SA1123.severity = suggestion
+
+# SA1130: Use lambda syntax
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1130.md
+dotnet_diagnostic.SA1130.severity = suggestion
+
+# SA1133: Do not combine attributes
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1133.md
+dotnet_diagnostic.SA1133.severity = suggestion
+
+# SA1203: Constants must appear before fields
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1203.md
+dotnet_diagnostic.SA1203.severity = suggestion
+
+# SA1204: Static elements must appear before instance elements
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1204.md
+dotnet_diagnostic.SA1204.severity = suggestion
+
+# SA1400: Access modifier must be declared
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1400.md
+dotnet_diagnostic.SA1400.severity = suggestion
+
+# SA1401: Fields must be private
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
+dotnet_diagnostic.SA1401.severity = suggestion
+
+# SA1411: Attribute constructor must not use unnecessary parenthesis
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1411.md
+dotnet_diagnostic.SA1411.severity = suggestion
+
+# SA1505: Opening braces must not be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1505.md
+dotnet_diagnostic.SA1505.severity = suggestion
+
+# SA1507: Code must not contain multiple blank lines in a row
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1507.md
+dotnet_diagnostic.SA1507.severity = suggestion
+
+# SA1508: Closing braces must not be preceded by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1508.md
+dotnet_diagnostic.SA1508.severity = suggestion
+
+# SA1510: Chained statement blocks must not be preceded by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1510.md
+dotnet_diagnostic.SA1510.severity = suggestion
+
+# SA1512: Single line comments must not be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1512.md
+dotnet_diagnostic.SA1512.severity = suggestion
+
+# SA1513: Closing brace must be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1513.md
+dotnet_diagnostic.SA1513.severity = suggestion
+
+# SA1514: Element documentation header must be preceded by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1514.md
+dotnet_diagnostic.SA1514.severity = suggestion
+
+# SA1515: Single line comment must be preceded by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1515.md
+dotnet_diagnostic.SA1515.severity = suggestion
+
+# SA1517: Code must not contain blank lines at start of file
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1517.md
+dotnet_diagnostic.SA1517.severity = suggestion
+
+# SA1518: Use line endings correctly at end of file
+https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1518.md
+dotnet_diagnostic.SA1518.severity = suggestion
+
+# SA1626: Single line comments must not use documentation style slashes
+https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1626.md
+dotnet_diagnostic.SA1626.severity = suggestion
+
+#### Meziantou.Analyzer rules ####
+
+# MA0001: StringComparison is missing
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0001.md
+dotnet_diagnostic.MA0001.severity = suggestion
+
+# MA0003: Add parameter name to improve readability
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0003.md
+dotnet_diagnostic.MA0003.severity = suggestion
+
+# MA0004: Use Task.ConfigureAwait(false)
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0004.md
+dotnet_diagnostic.MA0004.severity = suggestion
+
+# MA0011: IFormatProvider is missing
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0011.md
+dotnet_diagnostic.MA0011.severity = suggestion
+
+# MA0042: Do not use blocking calls in an async method
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0042.md
+dotnet_diagnostic.MA0042.severity = suggestion
+
+# MA0043: Use nameof operator in ArgumentException
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0043.md
+dotnet_diagnostic.MA0043.severity = suggestion
+
+# MA0046: Use EventHandler to declare events
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0046.md
+dotnet_diagnostic.MA0046.severity = suggestion
+
+# MA0053: Make class sealed
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md
+dotnet_diagnostic.MA0053.severity = suggestion
+
+# MA0060: The value returned by Stream.Read/Stream.ReadAsync is not used
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0060.md
+dotnet_diagnostic.MA0060.severity = suggestion
+
+# MA0074: Avoid implicit culture-sensitive methods
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0074.md
+dotnet_diagnostic.MA0074.severity = suggestion
+
+# MA0075: Do not use implicit culture-sensitive ToString
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0075.md
+dotnet_diagnostic.MA0075.severity = suggestion
+
+# MA0076: Do not use implicit culture-sensitive ToString in interpolated strings
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0076.md
+dotnet_diagnostic.MA0076.severity = suggestion
+
+# MA0099 - Use Explicit enum value instead of 0
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0099.md
+dotnet_diagnostic.MA0099.severity = suggestion
+
+# MA0101: String contains an implicit end of line character
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0101.md
+dotnet_diagnostic.MA0101.severity = suggestion
+
+# MA0110: Use the Regex source generator
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0110.md
+dotnet_diagnostic.MA0110.severity = suggestion
+
#### .NET Compiler Platform analysers rules ####
+# CA1031: Do not catch general exception types
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1031
+dotnet_diagnostic.CA1031.severity = suggestion
+
+# CA1052: Static holder types should be Static or NotInheritable
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1052
+dotnet_diagnostic.CA1052.severity = suggestion
+
+# CA1062: Validate arguments of public methods
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1062
+dotnet_diagnostic.CA1062.severity = suggestion
+
+# CA1063: Implement IDisposable correctly
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1063
+dotnet_diagnostic.CA1063.severity = suggestion
+
+# CA1307: Specify StringComparison for clarity
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1307
+dotnet_diagnostic.CA1307.severity = suggestion
+
+# CA1310: Specify StringComparison for correctness
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1310
+dotnet_diagnostic.CA1310.severity = suggestion
+
+# CA1507: Use nameof in place of string
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1507
+dotnet_diagnostic.CA1507.severity = suggestion
+
+# CA1827: Do not use Count()/LongCount() when Any() can be used
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1827
+dotnet_diagnostic.CA1827.severity = suggestion
+
+# CA1812: Avoid uninstantiated internal classes
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1812
+dotnet_diagnostic.CA1812.severity = suggestion
+
+# CA1816: Call GC.SuppressFinalize correctly
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1816
+dotnet_diagnostic.CA1816.severity = suggestion
+
+# CA1822: Mark members as static
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822
+dotnet_diagnostic.CA1822.severity = suggestion
+
+# CA1840: Use Environment.CurrentManagedThreadId instead of Thread.CurrentThread.ManagedThreadId
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1840
+dotnet_diagnostic.CA1840.severity = suggestion
+
+# CA1851: Possible multiple enumerations of IEnumerable collection
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1851
+dotnet_diagnostic.CA1851.severity = suggestion
+
+# CA1859: Use concrete types when possible for improved performance
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1859
+dotnet_diagnostic.CA1859.severity = suggestion
+
+# CA2000: Dispose objects before losing scope
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2000
+dotnet_diagnostic.CA2000.severity = suggestion
+
+# CA2007: Do not directly await a Task
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2007
+dotnet_diagnostic.CA2007.severity = suggestion
+
+# CA2008: Do not create tasks without passing a TaskScheduler
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2008
+dotnet_diagnostic.CA2008.severity = suggestion
+
+# CA2201: Do not raise reserved exception types
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2201
+dotnet_diagnostic.CA2201.severity = suggestion
+
+# CA2213: Disposable fields should be disposed
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2213
+dotnet_diagnostic.CA2213.severity = suggestion
+
+# CA2234: Pass System.Uri objects instead of strings
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2234
+dotnet_diagnostic.CA2234.severity = suggestion
+
# IDE0007: Use var instead of explicit type
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007
dotnet_diagnostic.IDE0007.severity = suggestion
diff --git a/test/Renci.SshNet.IntegrationTests/HostConfig.cs b/test/Renci.SshNet.IntegrationTests/HostConfig.cs
index 817bd7026..d501dd384 100644
--- a/test/Renci.SshNet.IntegrationTests/HostConfig.cs
+++ b/test/Renci.SshNet.IntegrationTests/HostConfig.cs
@@ -98,18 +98,4 @@ public void Write(ScpClient scpClient, string path)
}
}
}
-
- public class HostEntry
- {
- public HostEntry(IPAddress ipAddress, string hostName)
- {
- IPAddress = ipAddress;
- HostName = hostName;
- Aliases = new List();
- }
-
- public IPAddress IPAddress { get; private set; }
- public string HostName { get; set; }
- public List Aliases { get; }
- }
}
diff --git a/test/Renci.SshNet.IntegrationTests/HostEntry.cs b/test/Renci.SshNet.IntegrationTests/HostEntry.cs
new file mode 100644
index 000000000..9127e820a
--- /dev/null
+++ b/test/Renci.SshNet.IntegrationTests/HostEntry.cs
@@ -0,0 +1,18 @@
+using System.Net;
+
+namespace Renci.SshNet.IntegrationTests
+{
+ public sealed class HostEntry
+ {
+ public HostEntry(IPAddress ipAddress, string hostName)
+ {
+ IPAddress = ipAddress;
+ HostName = hostName;
+ Aliases = new List();
+ }
+
+ public IPAddress IPAddress { get; private set; }
+ public string HostName { get; set; }
+ public List Aliases { get; }
+ }
+}
diff --git a/test/Renci.SshNet.IntegrationTests/RemoteSshd.cs b/test/Renci.SshNet.IntegrationTests/RemoteSshd.cs
index b9a32e67a..14614b406 100644
--- a/test/Renci.SshNet.IntegrationTests/RemoteSshd.cs
+++ b/test/Renci.SshNet.IntegrationTests/RemoteSshd.cs
@@ -1,6 +1,4 @@
-using Renci.SshNet.TestTools.OpenSSH;
-
-namespace Renci.SshNet.IntegrationTests
+namespace Renci.SshNet.IntegrationTests
{
internal class RemoteSshd
{
@@ -42,217 +40,4 @@ public RemoteSshd Restart()
return this;
}
}
-
- internal class RemoteSshdConfig
- {
- private const string SshdConfigFilePath = "/etc/ssh/sshd_config";
- private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true);
-
- private readonly RemoteSshd _remoteSshd;
- private readonly IConnectionInfoFactory _connectionInfoFactory;
- private readonly SshdConfig _config;
-
- public RemoteSshdConfig(RemoteSshd remoteSshd, IConnectionInfoFactory connectionInfoFactory)
- {
- _remoteSshd = remoteSshd;
- _connectionInfoFactory = connectionInfoFactory;
-
- using (var client = new ScpClient(_connectionInfoFactory.Create()))
- {
- client.Connect();
-
- using (var memoryStream = new MemoryStream())
- {
- client.Download(SshdConfigFilePath, memoryStream);
-
- memoryStream.Position = 0;
- _config = SshdConfig.LoadFrom(memoryStream, Encoding.UTF8);
- }
- }
- }
-
- ///
- /// Specifies whether challenge-response authentication is allowed.
- ///
- /// to allow challenge-response authentication.
- ///
- /// The current instance.
- ///
- public RemoteSshdConfig WithChallengeResponseAuthentication(bool? value)
- {
- _config.ChallengeResponseAuthentication = value;
- return this;
- }
-
- ///
- /// Specifies whether to allow keyboard-interactive authentication.
- ///
- /// to allow keyboard-interactive authentication.
- ///
- /// The current instance.
- ///
- public RemoteSshdConfig WithKeyboardInteractiveAuthentication(bool value)
- {
- _config.KeyboardInteractiveAuthentication = value;
- return this;
- }
-
- ///
- /// Specifies whether sshd should print /etc/motd when a user logs in interactively.
- ///
- /// if sshd should print /etc/motd when a user logs in interactively.
- ///
- /// The current instance.
- ///
- public RemoteSshdConfig PrintMotd(bool? value = true)
- {
- _config.PrintMotd = value;
- return this;
- }
-
- ///
- /// Specifies whether TCP forwarding is permitted.
- ///
- /// to allow TCP forwarding.
- ///
- /// The current instance.
- ///
- public RemoteSshdConfig AllowTcpForwarding(bool? value = true)
- {
- _config.AllowTcpForwarding = value;
- return this;
- }
-
- public RemoteSshdConfig WithAuthenticationMethods(string user, string authenticationMethods)
- {
- var sshNetMatch = _config.Matches.FirstOrDefault(m => m.Users.Contains(user));
- if (sshNetMatch == null)
- {
- sshNetMatch = new Match(new[] { user }, new string[0]);
- _config.Matches.Add(sshNetMatch);
- }
-
- sshNetMatch.AuthenticationMethods = authenticationMethods;
-
- return this;
- }
-
- public RemoteSshdConfig ClearCiphers()
- {
- _config.Ciphers.Clear();
- return this;
- }
-
- public RemoteSshdConfig AddCipher(Cipher cipher)
- {
- _config.Ciphers.Add(cipher);
- return this;
- }
-
- public RemoteSshdConfig ClearKeyExchangeAlgorithms()
- {
- _config.KeyExchangeAlgorithms.Clear();
- return this;
- }
-
- public RemoteSshdConfig AddKeyExchangeAlgorithm(KeyExchangeAlgorithm keyExchangeAlgorithm)
- {
- _config.KeyExchangeAlgorithms.Add(keyExchangeAlgorithm);
- return this;
- }
-
- public RemoteSshdConfig ClearPublicKeyAcceptedAlgorithms()
- {
- _config.PublicKeyAcceptedAlgorithms.Clear();
- return this;
- }
-
- public RemoteSshdConfig AddPublicKeyAcceptedAlgorithm(PublicKeyAlgorithm publicKeyAlgorithm)
- {
- _config.PublicKeyAcceptedAlgorithms.Add(publicKeyAlgorithm);
- return this;
- }
-
- public RemoteSshdConfig ClearMessageAuthenticationCodeAlgorithms()
- {
- _config.MessageAuthenticationCodeAlgorithms.Clear();
- return this;
- }
-
- public RemoteSshdConfig AddMessageAuthenticationCodeAlgorithm(MessageAuthenticationCodeAlgorithm messageAuthenticationCodeAlgorithm)
- {
- _config.MessageAuthenticationCodeAlgorithms.Add(messageAuthenticationCodeAlgorithm);
- return this;
- }
-
- public RemoteSshdConfig ClearHostKeyAlgorithms()
- {
- _config.HostKeyAlgorithms.Clear();
- return this;
- }
-
- public RemoteSshdConfig AddHostKeyAlgorithm(HostKeyAlgorithm hostKeyAlgorithm)
- {
- _config.HostKeyAlgorithms.Add(hostKeyAlgorithm);
- return this;
- }
-
- public RemoteSshdConfig ClearSubsystems()
- {
- _config.Subsystems.Clear();
- return this;
- }
-
- public RemoteSshdConfig AddSubsystem(Subsystem subsystem)
- {
- _config.Subsystems.Add(subsystem);
- return this;
- }
-
- public RemoteSshdConfig WithLogLevel(LogLevel logLevel)
- {
- _config.LogLevel = logLevel;
- return this;
- }
-
- public RemoteSshdConfig WithUsePAM(bool usePAM)
- {
- _config.UsePAM = usePAM;
- return this;
- }
-
- public RemoteSshdConfig ClearHostKeyFiles()
- {
- _config.HostKeyFiles.Clear();
- return this;
- }
-
- public RemoteSshdConfig AddHostKeyFile(string hostKeyFile)
- {
- _config.HostKeyFiles.Add(hostKeyFile);
- return this;
- }
-
- public RemoteSshd Update()
- {
- using (var client = new ScpClient(_connectionInfoFactory.Create()))
- {
- client.Connect();
-
- using (var memoryStream = new MemoryStream())
- using (var sw = new StreamWriter(memoryStream, Utf8NoBom))
- {
- sw.NewLine = "\n";
- _config.SaveTo(sw);
- sw.Flush();
-
- memoryStream.Position = 0;
-
- client.Upload(memoryStream, SshdConfigFilePath);
- }
- }
-
- return _remoteSshd;
- }
- }
}
diff --git a/test/Renci.SshNet.IntegrationTests/RemoteSshdConfig.cs b/test/Renci.SshNet.IntegrationTests/RemoteSshdConfig.cs
new file mode 100644
index 000000000..391449973
--- /dev/null
+++ b/test/Renci.SshNet.IntegrationTests/RemoteSshdConfig.cs
@@ -0,0 +1,217 @@
+using Renci.SshNet.TestTools.OpenSSH;
+
+namespace Renci.SshNet.IntegrationTests
+{
+ internal sealed class RemoteSshdConfig
+ {
+ private const string SshdConfigFilePath = "/etc/ssh/sshd_config";
+ private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true);
+
+ private readonly RemoteSshd _remoteSshd;
+ private readonly IConnectionInfoFactory _connectionInfoFactory;
+ private readonly SshdConfig _config;
+
+ public RemoteSshdConfig(RemoteSshd remoteSshd, IConnectionInfoFactory connectionInfoFactory)
+ {
+ _remoteSshd = remoteSshd;
+ _connectionInfoFactory = connectionInfoFactory;
+
+ using (var client = new ScpClient(_connectionInfoFactory.Create()))
+ {
+ client.Connect();
+
+ using (var memoryStream = new MemoryStream())
+ {
+ client.Download(SshdConfigFilePath, memoryStream);
+
+ memoryStream.Position = 0;
+ _config = SshdConfig.LoadFrom(memoryStream, Encoding.UTF8);
+ }
+ }
+ }
+
+ ///
+ /// Specifies whether challenge-response authentication is allowed.
+ ///
+ /// to allow challenge-response authentication.
+ ///
+ /// The current instance.
+ ///
+ public RemoteSshdConfig WithChallengeResponseAuthentication(bool? value)
+ {
+ _config.ChallengeResponseAuthentication = value;
+ return this;
+ }
+
+ ///
+ /// Specifies whether to allow keyboard-interactive authentication.
+ ///
+ /// to allow keyboard-interactive authentication.
+ ///
+ /// The current instance.
+ ///
+ public RemoteSshdConfig WithKeyboardInteractiveAuthentication(bool value)
+ {
+ _config.KeyboardInteractiveAuthentication = value;
+ return this;
+ }
+
+ ///
+ /// Specifies whether sshd should print /etc/motd when a user logs in interactively.
+ ///
+ /// if sshd should print /etc/motd when a user logs in interactively.
+ ///
+ /// The current instance.
+ ///
+ public RemoteSshdConfig PrintMotd(bool? value = true)
+ {
+ _config.PrintMotd = value;
+ return this;
+ }
+
+ ///
+ /// Specifies whether TCP forwarding is permitted.
+ ///
+ /// to allow TCP forwarding.
+ ///
+ /// The current instance.
+ ///
+ public RemoteSshdConfig AllowTcpForwarding(bool? value = true)
+ {
+ _config.AllowTcpForwarding = value;
+ return this;
+ }
+
+ public RemoteSshdConfig WithAuthenticationMethods(string user, string authenticationMethods)
+ {
+ var sshNetMatch = _config.Matches.Find(m => m.Users.Contains(user));
+ if (sshNetMatch is null)
+ {
+ sshNetMatch = new Match(new[] { user }, Array.Empty());
+ _config.Matches.Add(sshNetMatch);
+ }
+
+ sshNetMatch.AuthenticationMethods = authenticationMethods;
+
+ return this;
+ }
+
+ public RemoteSshdConfig ClearCiphers()
+ {
+ _config.Ciphers.Clear();
+ return this;
+ }
+
+ public RemoteSshdConfig AddCipher(Cipher cipher)
+ {
+ _config.Ciphers.Add(cipher);
+ return this;
+ }
+
+ public RemoteSshdConfig ClearKeyExchangeAlgorithms()
+ {
+ _config.KeyExchangeAlgorithms.Clear();
+ return this;
+ }
+
+ public RemoteSshdConfig AddKeyExchangeAlgorithm(KeyExchangeAlgorithm keyExchangeAlgorithm)
+ {
+ _config.KeyExchangeAlgorithms.Add(keyExchangeAlgorithm);
+ return this;
+ }
+
+ public RemoteSshdConfig ClearPublicKeyAcceptedAlgorithms()
+ {
+ _config.PublicKeyAcceptedAlgorithms.Clear();
+ return this;
+ }
+
+ public RemoteSshdConfig AddPublicKeyAcceptedAlgorithm(PublicKeyAlgorithm publicKeyAlgorithm)
+ {
+ _config.PublicKeyAcceptedAlgorithms.Add(publicKeyAlgorithm);
+ return this;
+ }
+
+ public RemoteSshdConfig ClearMessageAuthenticationCodeAlgorithms()
+ {
+ _config.MessageAuthenticationCodeAlgorithms.Clear();
+ return this;
+ }
+
+ public RemoteSshdConfig AddMessageAuthenticationCodeAlgorithm(MessageAuthenticationCodeAlgorithm messageAuthenticationCodeAlgorithm)
+ {
+ _config.MessageAuthenticationCodeAlgorithms.Add(messageAuthenticationCodeAlgorithm);
+ return this;
+ }
+
+ public RemoteSshdConfig ClearHostKeyAlgorithms()
+ {
+ _config.HostKeyAlgorithms.Clear();
+ return this;
+ }
+
+ public RemoteSshdConfig AddHostKeyAlgorithm(HostKeyAlgorithm hostKeyAlgorithm)
+ {
+ _config.HostKeyAlgorithms.Add(hostKeyAlgorithm);
+ return this;
+ }
+
+ public RemoteSshdConfig ClearSubsystems()
+ {
+ _config.Subsystems.Clear();
+ return this;
+ }
+
+ public RemoteSshdConfig AddSubsystem(Subsystem subsystem)
+ {
+ _config.Subsystems.Add(subsystem);
+ return this;
+ }
+
+ public RemoteSshdConfig WithLogLevel(LogLevel logLevel)
+ {
+ _config.LogLevel = logLevel;
+ return this;
+ }
+
+ public RemoteSshdConfig WithUsePAM(bool usePAM)
+ {
+ _config.UsePAM = usePAM;
+ return this;
+ }
+
+ public RemoteSshdConfig ClearHostKeyFiles()
+ {
+ _config.HostKeyFiles.Clear();
+ return this;
+ }
+
+ public RemoteSshdConfig AddHostKeyFile(string hostKeyFile)
+ {
+ _config.HostKeyFiles.Add(hostKeyFile);
+ return this;
+ }
+
+ public RemoteSshd Update()
+ {
+ using (var client = new ScpClient(_connectionInfoFactory.Create()))
+ {
+ client.Connect();
+
+ using (var memoryStream = new MemoryStream())
+ using (var sw = new StreamWriter(memoryStream, Utf8NoBom))
+ {
+ sw.NewLine = "\n";
+ _config.SaveTo(sw);
+ sw.Flush();
+
+ memoryStream.Position = 0;
+
+ client.Upload(memoryStream, SshdConfigFilePath);
+ }
+ }
+
+ return _remoteSshd;
+ }
+ }
+}
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/.editorconfig b/test/Renci.SshNet.TestTools.OpenSSH/.editorconfig
new file mode 100644
index 000000000..15b989dbb
--- /dev/null
+++ b/test/Renci.SshNet.TestTools.OpenSSH/.editorconfig
@@ -0,0 +1,23 @@
+[*.cs]
+
+#### Meziantou.Analyzer rules ####
+
+# MA0001: StringComparison is missing
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0001.md
+#
+# TODO: Re-enable when issues are fixed
+dotnet_diagnostic.MA0001.severity = suggestion
+
+# MA0110: Use the Regex source generator
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0110.md
+dotnet_diagnostic.MA0110.severity = none
+
+#### .NET Compiler Platform analysers rules ####
+
+# CA1307: Specify StringComparison for clarity
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1307
+dotnet_diagnostic.CA1307.severity = none
+
+# CA1822: Mark members as static
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822
+dotnet_code_quality.CA1822.api_surface = none
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/Cipher.cs b/test/Renci.SshNet.TestTools.OpenSSH/Cipher.cs
index f7d8d8842..676504330 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/Cipher.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/Cipher.cs
@@ -1,6 +1,6 @@
namespace Renci.SshNet.TestTools.OpenSSH
{
- public class Cipher
+ public sealed class Cipher
{
public static readonly Cipher TripledesCbc = new Cipher("3des-cbc");
public static readonly Cipher Aes128Cbc = new Cipher("aes128-cbc");
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs
index 3eab2b199..e58867d79 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/BooleanFormatter.cs
@@ -1,6 +1,6 @@
namespace Renci.SshNet.TestTools.OpenSSH.Formatters
{
- internal class BooleanFormatter
+ internal sealed class BooleanFormatter
{
public string Format(bool value)
{
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs
index 2b8231111..dcddb8954 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/Int32Formatter.cs
@@ -2,7 +2,7 @@
namespace Renci.SshNet.TestTools.OpenSSH.Formatters
{
- internal class Int32Formatter
+ internal sealed class Int32Formatter
{
public string Format(int value)
{
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs
index f9f4bbf6f..9ab89c6a1 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/LogLevelFormatter.cs
@@ -1,6 +1,6 @@
namespace Renci.SshNet.TestTools.OpenSSH.Formatters
{
- internal class LogLevelFormatter
+ internal sealed class LogLevelFormatter
{
public string Format(LogLevel logLevel)
{
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs
index df2968be0..d8d7820ff 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/MatchFormatter.cs
@@ -1,6 +1,6 @@
namespace Renci.SshNet.TestTools.OpenSSH.Formatters
{
- internal class MatchFormatter
+ internal sealed class MatchFormatter
{
public string Format(Match match)
{
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs
index fcb74e266..91129aeb0 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/Formatters/SubsystemFormatter.cs
@@ -1,6 +1,6 @@
namespace Renci.SshNet.TestTools.OpenSSH.Formatters
{
- internal class SubsystemFormatter
+ internal sealed class SubsystemFormatter
{
public string Format(Subsystem subsystem)
{
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs b/test/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs
index 65807f462..23f41478b 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/HostKeyAlgorithm.cs
@@ -1,29 +1,29 @@
namespace Renci.SshNet.TestTools.OpenSSH
{
- public class HostKeyAlgorithm
+ public sealed class HostKeyAlgorithm
{
- public static readonly HostKeyAlgorithm EcdsaSha2Nistp256CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp256-cert-v01@openssh.com");
- public static readonly HostKeyAlgorithm EcdsaSha2Nistp384CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp384-cert-v01@openssh.com");
- public static readonly HostKeyAlgorithm EcdsaSha2Nistp521CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp521-cert-v01@openssh.com");
- public static readonly HostKeyAlgorithm SshEd25519CertV01OpenSSH = new HostKeyAlgorithm("ssh-ed25519-cert-v01@openssh.com");
- public static readonly HostKeyAlgorithm RsaSha2256CertV01OpenSSH = new HostKeyAlgorithm("rsa-sha2-256-cert-v01@openssh.com");
- public static readonly HostKeyAlgorithm RsaSha2512CertV01OpenSSH = new HostKeyAlgorithm("rsa-sha2-512-cert-v01@openssh.com");
- public static readonly HostKeyAlgorithm SshRsaCertV01OpenSSH = new HostKeyAlgorithm("ssh-rsa-cert-v01@openssh.com");
- public static readonly HostKeyAlgorithm EcdsaSha2Nistp256 = new HostKeyAlgorithm("ecdsa-sha2-nistp256");
- public static readonly HostKeyAlgorithm EcdsaSha2Nistp384 = new HostKeyAlgorithm("ecdsa-sha2-nistp384");
- public static readonly HostKeyAlgorithm EcdsaSha2Nistp521 = new HostKeyAlgorithm("ecdsa-sha2-nistp521");
- public static readonly HostKeyAlgorithm SshEd25519 = new HostKeyAlgorithm("ssh-ed25519");
- public static readonly HostKeyAlgorithm RsaSha2512 = new HostKeyAlgorithm("rsa-sha2-512");
- public static readonly HostKeyAlgorithm RsaSha2256 = new HostKeyAlgorithm("rsa-sha2-256");
- public static readonly HostKeyAlgorithm SshRsa = new HostKeyAlgorithm("ssh-rsa");
+ public static readonly HostKeyAlgorithm EcdsaSha2Nistp256CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp256-cert-v01@openssh.com");
+ public static readonly HostKeyAlgorithm EcdsaSha2Nistp384CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp384-cert-v01@openssh.com");
+ public static readonly HostKeyAlgorithm EcdsaSha2Nistp521CertV01OpenSSH = new HostKeyAlgorithm("ecdsa-sha2-nistp521-cert-v01@openssh.com");
+ public static readonly HostKeyAlgorithm SshEd25519CertV01OpenSSH = new HostKeyAlgorithm("ssh-ed25519-cert-v01@openssh.com");
+ public static readonly HostKeyAlgorithm RsaSha2256CertV01OpenSSH = new HostKeyAlgorithm("rsa-sha2-256-cert-v01@openssh.com");
+ public static readonly HostKeyAlgorithm RsaSha2512CertV01OpenSSH = new HostKeyAlgorithm("rsa-sha2-512-cert-v01@openssh.com");
+ public static readonly HostKeyAlgorithm SshRsaCertV01OpenSSH = new HostKeyAlgorithm("ssh-rsa-cert-v01@openssh.com");
+ public static readonly HostKeyAlgorithm EcdsaSha2Nistp256 = new HostKeyAlgorithm("ecdsa-sha2-nistp256");
+ public static readonly HostKeyAlgorithm EcdsaSha2Nistp384 = new HostKeyAlgorithm("ecdsa-sha2-nistp384");
+ public static readonly HostKeyAlgorithm EcdsaSha2Nistp521 = new HostKeyAlgorithm("ecdsa-sha2-nistp521");
+ public static readonly HostKeyAlgorithm SshEd25519 = new HostKeyAlgorithm("ssh-ed25519");
+ public static readonly HostKeyAlgorithm RsaSha2512 = new HostKeyAlgorithm("rsa-sha2-512");
+ public static readonly HostKeyAlgorithm RsaSha2256 = new HostKeyAlgorithm("rsa-sha2-256");
+ public static readonly HostKeyAlgorithm SshRsa = new HostKeyAlgorithm("ssh-rsa");
public static readonly HostKeyAlgorithm SshDss = new HostKeyAlgorithm("ssh-dss");
public HostKeyAlgorithm(string name)
{
- Name = name;
- }
+ Name = name;
+ }
- public string Name { get; }
+ public string Name { get; }
public override bool Equals(object? obj)
{
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs b/test/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs
index 4701103f0..e63d8ea17 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/KeyExchangeAlgorithm.cs
@@ -1,6 +1,6 @@
namespace Renci.SshNet.TestTools.OpenSSH
{
- public class KeyExchangeAlgorithm
+ public sealed class KeyExchangeAlgorithm
{
public static readonly KeyExchangeAlgorithm DiffieHellmanGroup1Sha1 = new KeyExchangeAlgorithm("diffie-hellman-group1-sha1");
public static readonly KeyExchangeAlgorithm DiffieHellmanGroup14Sha1 = new KeyExchangeAlgorithm("diffie-hellman-group14-sha1");
@@ -16,7 +16,6 @@ public class KeyExchangeAlgorithm
public static readonly KeyExchangeAlgorithm Curve25519Sha256Libssh = new KeyExchangeAlgorithm("curve25519-sha256@libssh.org");
public static readonly KeyExchangeAlgorithm Sntrup4591761x25519Sha512 = new KeyExchangeAlgorithm("sntrup4591761x25519-sha512@tinyssh.org");
-
public KeyExchangeAlgorithm(string name)
{
Name = name;
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/Match.cs b/test/Renci.SshNet.TestTools.OpenSSH/Match.cs
index 16cd5073d..d44dc04a3 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/Match.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/Match.cs
@@ -1,6 +1,6 @@
namespace Renci.SshNet.TestTools.OpenSSH
{
- public class Match
+ public sealed class Match
{
public Match(string[] users, string[] addresses)
{
@@ -16,6 +16,11 @@ public Match(string[] users, string[] addresses)
public void WriteTo(TextWriter writer)
{
+ if (writer is null)
+ {
+ throw new ArgumentNullException(nameof(writer));
+ }
+
writer.Write("Match ");
if (Users.Length > 0)
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs b/test/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs
index 17bf0cf91..edb2466e9 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/MessageAuthenticationCodeAlgorithm.cs
@@ -1,7 +1,8 @@
namespace Renci.SshNet.TestTools.OpenSSH
{
- public class MessageAuthenticationCodeAlgorithm
+ public sealed class MessageAuthenticationCodeAlgorithm
{
+#pragma warning disable SA1310 // Field names should not contain underscore
public static readonly MessageAuthenticationCodeAlgorithm HmacMd5 = new MessageAuthenticationCodeAlgorithm("hmac-md5");
public static readonly MessageAuthenticationCodeAlgorithm HmacMd5_96 = new MessageAuthenticationCodeAlgorithm("hmac-md5-96");
public static readonly MessageAuthenticationCodeAlgorithm HmacRipemd160 = new MessageAuthenticationCodeAlgorithm("hmac-ripemd160");
@@ -20,6 +21,7 @@ public class MessageAuthenticationCodeAlgorithm
public static readonly MessageAuthenticationCodeAlgorithm HmacSha2_512_Etm = new MessageAuthenticationCodeAlgorithm("hmac-sha2-512-etm@openssh.com");
public static readonly MessageAuthenticationCodeAlgorithm Umac64_Etm = new MessageAuthenticationCodeAlgorithm("umac-64-etm@openssh.com");
public static readonly MessageAuthenticationCodeAlgorithm Umac128_Etm = new MessageAuthenticationCodeAlgorithm("umac-128-etm@openssh.com");
+#pragma warning restore SA1310 // Field names should not contain underscore
public MessageAuthenticationCodeAlgorithm(string name)
{
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs b/test/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs
index 292ace7f9..24e577b79 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/PublicKeyAlgorithm.cs
@@ -1,6 +1,6 @@
namespace Renci.SshNet.TestTools.OpenSSH
{
- public class PublicKeyAlgorithm
+ public sealed class PublicKeyAlgorithm
{
public static readonly PublicKeyAlgorithm SshEd25519 = new PublicKeyAlgorithm("ssh-ed25519");
public static readonly PublicKeyAlgorithm SshEd25519CertV01OpenSSH = new PublicKeyAlgorithm("ssh-ed25519-cert-v01@openssh.com");
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs b/test/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
index 7dc9f309c..5e3025711 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
@@ -6,9 +6,10 @@
namespace Renci.SshNet.TestTools.OpenSSH
{
- public class SshdConfig
+ public sealed class SshdConfig
{
- private static readonly Regex MatchRegex = new Regex($@"\s*Match\s+(User\s+(?[\S]+))?\s*(Address\s+(?[\S]+))?\s*", RegexOptions.Compiled);
+ private static readonly Regex MatchRegex = new Regex($@"\s*Match\s+(User\s+(?[\S]+))?\s*(Address\s+(?[\S]+))?\s*",
+ RegexOptions.Compiled | RegexOptions.ExplicitCapture);
private readonly SubsystemFormatter _subsystemFormatter;
private readonly Int32Formatter _int32Formatter;
@@ -50,7 +51,7 @@ private SshdConfig()
///
/// A list of private host key files used by sshd.
///
- public List HostKeyFiles { get; set; }
+ public List HostKeyFiles { get; }
///
/// Gets or sets a value specifying whether challenge-response authentication is allowed.
@@ -143,8 +144,83 @@ private SshdConfig()
///
public bool? AllowTcpForwarding { get; set; }
+ public static SshdConfig LoadFrom(Stream stream, Encoding encoding)
+ {
+ using (var sr = new StreamReader(stream, encoding))
+ {
+ var sshdConfig = new SshdConfig();
+
+ Match? currentMatchConfiguration = null;
+
+ string? line;
+ while ((line = sr.ReadLine()) != null)
+ {
+ // Skip empty lines
+ if (line.Length == 0)
+ {
+ continue;
+ }
+
+ // Skip comments
+ if (line[0] == '#')
+ {
+ continue;
+ }
+
+ var match = MatchRegex.Match(line);
+ if (match.Success)
+ {
+ var usersGroup = match.Groups["users"];
+ var addressesGroup = match.Groups["addresses"];
+ var users = usersGroup.Success ? usersGroup.Value.Split(',') : Array.Empty();
+ var addresses = addressesGroup.Success ? addressesGroup.Value.Split(',') : Array.Empty();
+
+ currentMatchConfiguration = new Match(users, addresses);
+ sshdConfig.Matches.Add(currentMatchConfiguration);
+ continue;
+ }
+
+ if (currentMatchConfiguration != null)
+ {
+ ProcessMatchOption(currentMatchConfiguration, line);
+ }
+ else
+ {
+ ProcessGlobalOption(sshdConfig, line);
+ }
+ }
+
+ if (sshdConfig.Ciphers == null)
+ {
+ // Obtain supported ciphers using ssh -Q cipher
+ }
+
+ if (sshdConfig.KeyExchangeAlgorithms == null)
+ {
+ // Obtain supports key exchange algorithms using ssh -Q kex
+ }
+
+ if (sshdConfig.HostKeyAlgorithms == null)
+ {
+ // Obtain supports host key algorithms using ssh -Q key
+ }
+
+ if (sshdConfig.MessageAuthenticationCodeAlgorithms == null)
+ {
+ // Obtain supported MACs using ssh -Q mac
+ }
+
+ return sshdConfig;
+ }
+ }
+
public void SaveTo(TextWriter writer)
{
+ if (writer is null)
+ {
+ throw new ArgumentNullException(nameof(writer));
+ }
+
writer.WriteLine("Protocol " + Protocol);
writer.WriteLine("Port " + _int32Formatter.Format(Port));
if (HostKeyFiles.Count > 0)
@@ -215,84 +291,13 @@ public void SaveTo(TextWriter writer)
{
writer.WriteLine("PubkeyAcceptedAlgorithms " + string.Join(",", PublicKeyAcceptedAlgorithms.Select(c => c.Name).ToArray()));
}
-
+
foreach (var match in Matches)
{
_matchFormatter.Format(match, writer);
}
}
- public static SshdConfig LoadFrom(Stream stream, Encoding encoding)
- {
- using (var sr = new StreamReader(stream, encoding))
- {
- var sshdConfig = new SshdConfig();
-
- Match? currentMatchConfiguration = null;
-
- string? line;
- while ((line = sr.ReadLine()) != null)
- {
- // Skip empty lines
- if (line.Length == 0)
- {
- continue;
- }
-
- // Skip comments
- if (line[0] == '#')
- {
- continue;
- }
-
- var match = MatchRegex.Match(line);
- if (match.Success)
- {
- var usersGroup = match.Groups["users"];
- var addressesGroup = match.Groups["addresses"];
- var users = usersGroup.Success ? usersGroup.Value.Split(',') : Array.Empty();
- var addresses = addressesGroup.Success ? addressesGroup.Value.Split(',') : Array.Empty();
-
- currentMatchConfiguration = new Match(users, addresses);
- sshdConfig.Matches.Add(currentMatchConfiguration);
- continue;
- }
-
- if (currentMatchConfiguration != null)
- {
- ProcessMatchOption(currentMatchConfiguration, line);
- }
- else
- {
- ProcessGlobalOption(sshdConfig, line);
- }
- }
-
- if (sshdConfig.Ciphers == null)
- {
- // Obtain supported ciphers using ssh -Q cipher
- }
-
- if (sshdConfig.KeyExchangeAlgorithms == null)
- {
- // Obtain supports key exchange algorithms using ssh -Q kex
- }
-
- if (sshdConfig.HostKeyAlgorithms == null)
- {
- // Obtain supports host key algorithms using ssh -Q key
- }
-
- if (sshdConfig.MessageAuthenticationCodeAlgorithms == null)
- {
- // Obtain supported MACs using ssh -Q mac
- }
-
-
- return sshdConfig;
- }
- }
-
private static void ProcessGlobalOption(SshdConfig sshdConfig, string line)
{
var matchOptionRegex = new Regex(@"^\s*(?[\S]+)\s+(?.+?){1}\s*$");
@@ -315,7 +320,7 @@ private static void ProcessGlobalOption(SshdConfig sshdConfig, string line)
sshdConfig.Port = ToInt(value);
break;
case "HostKey":
- sshdConfig.HostKeyFiles = ParseCommaSeparatedValue(value);
+ ParseCommaSeparatedValue(sshdConfig.HostKeyFiles, value);
break;
case "ChallengeResponseAuthentication":
sshdConfig.ChallengeResponseAuthentication = ToBool(value);
@@ -324,7 +329,7 @@ private static void ProcessGlobalOption(SshdConfig sshdConfig, string line)
sshdConfig.KeyboardInteractiveAuthentication = ToBool(value);
break;
case "LogLevel":
- sshdConfig.LogLevel = (LogLevel) Enum.Parse(typeof(LogLevel), value, true);
+ sshdConfig.LogLevel = Enum.Parse(value, ignoreCase: true);
break;
case "Subsystem":
sshdConfig.Subsystems.Add(Subsystem.FromConfig(value));
@@ -382,7 +387,7 @@ private static void ProcessGlobalOption(SshdConfig sshdConfig, string line)
case "GatewayPorts":
break;
default:
- throw new Exception($"Global option '{name}' is not implemented.");
+ throw new NotSupportedException($"Global option '{name}' is not supported.");
}
}
@@ -399,10 +404,12 @@ private static List ParseCiphers(string value)
{
var cipherNames = value.Split(',');
var ciphers = new List(cipherNames.Length);
+
foreach (var cipherName in cipherNames)
{
ciphers.Add(new Cipher(cipherName.Trim()));
}
+
return ciphers;
}
@@ -410,21 +417,30 @@ private static List ParseKeyExchangeAlgorithms(string valu
{
var kexNames = value.Split(',');
var keyExchangeAlgorithms = new List(kexNames.Length);
+
foreach (var kexName in kexNames)
{
keyExchangeAlgorithms.Add(new KeyExchangeAlgorithm(kexName.Trim()));
}
+
return keyExchangeAlgorithms;
}
public static List ParsePublicKeyAcceptedAlgorithms(string value)
{
+ if (value is null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
var publicKeyAlgorithmNames = value.Split(',');
var publicKeyAlgorithms = new List(publicKeyAlgorithmNames.Length);
+
foreach (var publicKeyAlgorithmName in publicKeyAlgorithmNames)
{
publicKeyAlgorithms.Add(new PublicKeyAlgorithm(publicKeyAlgorithmName.Trim()));
}
+
return publicKeyAlgorithms;
}
@@ -432,10 +448,12 @@ private static List ParseHostKeyAlgorithms(string value)
{
var algorithmNames = value.Split(',');
var hostKeyAlgorithms = new List(algorithmNames.Length);
+
foreach (var algorithmName in algorithmNames)
{
hostKeyAlgorithms.Add(new HostKeyAlgorithm(algorithmName.Trim()));
}
+
return hostKeyAlgorithms;
}
@@ -443,10 +461,12 @@ private static List ParseMacs(string value)
{
var macNames = value.Split(',');
var macAlgorithms = new List(macNames.Length);
+
foreach (var algorithmName in macNames)
{
macAlgorithms.Add(new MessageAuthenticationCodeAlgorithm(algorithmName.Trim()));
}
+
return macAlgorithms;
}
@@ -472,15 +492,14 @@ private static void ProcessMatchOption(Match matchConfiguration, string line)
matchConfiguration.AuthenticationMethods = value;
break;
default:
- throw new Exception($"Match option '{name}' is not implemented.");
+ throw new NotSupportedException($"Match option '{name}' is not supported.");
}
}
-
- private static List ParseCommaSeparatedValue(string value)
+ private static void ParseCommaSeparatedValue(List list, string value)
{
var values = value.Split(',');
- return new List(values);
+ list.AddRange(values);
}
private static bool ToBool(string value)
@@ -492,7 +511,8 @@ private static bool ToBool(string value)
case "no":
return false;
default:
- throw new Exception($"Value '{value}' cannot be mapped to a boolean.");
+ throw new ArgumentException($"Value '{value}' cannot be mapped to a boolean.",
+ nameof(value));
}
}
diff --git a/test/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs b/test/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs
index 4d1155105..140bc35bf 100644
--- a/test/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs
+++ b/test/Renci.SshNet.TestTools.OpenSSH/Subsystem.cs
@@ -2,7 +2,7 @@
namespace Renci.SshNet.TestTools.OpenSSH
{
- public class Subsystem
+ public sealed class Subsystem
{
public Subsystem(string name, string command)
{
@@ -14,11 +14,6 @@ public Subsystem(string name, string command)
public string Command { get; set; }
- public void WriteTo(TextWriter writer)
- {
- writer.WriteLine(Name + "=" + Command);
- }
-
public static Subsystem FromConfig(string value)
{
var subSystemValueRegex = new Regex(@"^\s*(?[\S]+)\s+(?.+?){1}\s*$");
@@ -35,7 +30,18 @@ public static Subsystem FromConfig(string value)
return new Subsystem(name, command);
}
- throw new Exception($"'{value}' not recognized as value for Subsystem.");
+ throw new ArgumentException($"'{value}' not recognized as value for Subsystem.",
+ nameof(value));
+ }
+
+ public void WriteTo(TextWriter writer)
+ {
+ if (writer is null)
+ {
+ throw new ArgumentNullException(nameof(writer));
+ }
+
+ writer.WriteLine(Name + "=" + Command);
}
}
}
diff --git a/test/Renci.SshNet.Tests/.editorconfig b/test/Renci.SshNet.Tests/.editorconfig
index b94e29112..aa6a9ba37 100644
--- a/test/Renci.SshNet.Tests/.editorconfig
+++ b/test/Renci.SshNet.Tests/.editorconfig
@@ -1,5 +1,59 @@
[*.cs]
+#### Sonar rules ####
+
+# S108: Nested blocks of code should not be left empty
+# https://rules.sonarsource.com/csharp/RSPEC-108/
+dotnet_diagnostic.S108.severity = suggestion
+
+# S125: Sections of code should not be commented out
+# https://rules.sonarsource.com/csharp/RSPEC-125/
+dotnet_diagnostic.S125.severity = none
+
+# S1144: Unused private types or members should be removed
+# https://rules.sonarsource.com/csharp/RSPEC-1144/
+dotnet_diagnostic.S1144.severity = none
+
+# S1186: Methods should not be empty
+# https://rules.sonarsource.com/csharp/RSPEC-1186/
+dotnet_diagnostic.S1186.severity = none
+
+# S1607: Tests should not be ignored
+# https://rules.sonarsource.com/csharp/RSPEC-1607/
+dotnet_diagnostic.S1607.severity = none
+
+# S2094: Classes should not be empty
+# https://rules.sonarsource.com/csharp/RSPEC-2094/
+dotnet_diagnostic.S2094.severity = none
+
+# S2187: Test classes should contain at least one test case
+# https://rules.sonarsource.com/csharp/RSPEC-2187/
+dotnet_diagnostic.S2187.severity = none
+
+# S2292: Trivial properties should be auto-implemented
+# https://rules.sonarsource.com/csharp/RSPEC-2292/
+dotnet_diagnostic.S2292.severity = none
+
+# S2925: "Thread.Sleep" should not be used in tests
+# https://rules.sonarsource.com/csharp/RSPEC-2925/
+dotnet_diagnostic.S2925.severity = none
+
+# S3415: Assertion arguments should be passed in the correct order
+# https://rules.sonarsource.com/csharp/RSPEC-3415/
+dotnet_diagnostic.S3415.severity = none
+
+# S3881: "IDisposable" should be implemented correctly
+# https://rules.sonarsource.com/csharp/RSPEC-3881/
+dotnet_diagnostic.S3881.severity = none
+
+# S4144: Methods should not have identical implementations
+# https://rules.sonarsource.com/csharp/RSPEC-4144/
+dotnet_diagnostic.S4144.severity = none
+
+# S4158: Empty collections should not be accessed or iterated
+# https://rules.sonarsource.com/csharp/RSPEC-4158/
+dotnet_diagnostic.S4158.severity = none
+
#### SYSLIB diagnostics ####
# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time
@@ -9,8 +63,306 @@ dotnet_diagnostic.SYSLIB1045.severity = none
### StyleCop Analyzers rules ###
+# SA1000: Keywords must be spaced correctly
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1000.md
+dotnet_diagnostic.SA1000.severity = suggestion
+
+# SA1001: Commas must be spaced correctly
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1001.md
+dotnet_diagnostic.SA1001.severity = suggestion
+
+# SA1002: Semicolons must be spaced correctly
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1002.md
+dotnet_diagnostic.SA1002.severity = suggestion
+
+# SA1004: Documentation lines must begin with single space
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1004.md
+dotnet_diagnostic.SA1004.severity = suggestion
+
+# SA1005: Single line comments must begin with single space
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1005.md
+dotnet_diagnostic.SA1005.severity = suggestion
+
+# SA1012: Opening braces must be spaced correctly
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1012.md
+dotnet_diagnostic.SA1012.severity = suggestion
+
+# SA1013: Closing braces must be spaced correctly
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1013.md
+dotnet_diagnostic.SA1013.severity = suggestion
+
+# SA1025: Code must not contain multiple whitespace in a row
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1025.md
+dotnet_diagnostic.SA1025.severity = suggestion
+
+# SA1026: Code must not contain space after new keyword in implicitly typed array allocation
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1026.md
+dotnet_diagnostic.SA1026.severity = suggestion
+
+# SA1028: Code must not contain trailing whitespace
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1028.md
+dotnet_diagnostic.SA1028.severity = suggestion
+
+# SA1111: Closing parenthesis must be on line of last parameter
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1111.md
+dotnet_diagnostic.SA1111.severity = suggestion
+
+# SA1117: Parameters must be on same line or separate lines
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1028.md
+dotnet_diagnostic.SA1117.severity = suggestion
+
+# SA1119: Statement must not use unnecessary parenthesis
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1119.md
+dotnet_diagnostic.SA1119.severity = suggestion
+
+# SA1120: Comments must contain text
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1120.md
+dotnet_diagnostic.SA1120.severity = suggestion
+
+# SA1122: Use String.Empty for empty strings
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1122.md
+dotnet_diagnostic.SA1122.severity = suggestion
+
+# SA1123:Do not place regions within elements
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1123.md
+dotnet_diagnostic.SA1123.severity = suggestion
+
+# SA1124: Do not use regions
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1124.md
+dotnet_diagnostic.SA1124.severity = suggestion
+
+# SA1128: Constructor initializer must be on own line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1128.md
+dotnet_diagnostic.SA1128.severity = suggestion
+
+# SA1129: Do not use default value type constructor
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1129.md
+dotnet_diagnostic.SA1129.severity = suggestion
+
+# SA1133: Do not combine attributes
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1133.md
+dotnet_diagnostic.SA1133.severity = suggestion
+
+# SA1137: Elements should have the same indentation
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1137.md
+dotnet_diagnostic.SA1137.severity = suggestion
+
+# SA1139: Use literals suffix notation instead of casting
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1139.md
+dotnet_diagnostic.SA1139.severity = suggestion
+
+# SA1204: Static elements must appear before instance elements
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1204.md
+dotnet_diagnostic.SA1204.severity = suggestion
+
+# SA1208: System using directives must be placed before other using directives
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1208.md
+dotnet_diagnostic.SA1208.severity = suggestion
+
+# SA1210: Using directives must be ordered alphabetically by namespace
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1210.md
+dotnet_diagnostic.SA1210.severity = suggestion
+
+# SA1214: Readonly elements must appear before non-readonly elements
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1214.md
+dotnet_diagnostic.SA1214.severity = suggestion
+
+# SA1306: Field names must begin with lower case letter
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md
+dotnet_diagnostic.SA1306.severity = suggestion
+
+# SA1310: Field names must not contain underscore
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1310.md
+dotnet_diagnostic.SA1310.severity = suggestion
+
+# SA1400: Access modifier must be declared
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1400.md
+dotnet_diagnostic.SA1400.severity = suggestion
+
+# SA1401: Fields must be private
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
+dotnet_diagnostic.SA1401.severity = suggestion
+
+# SA1402: File may only contain a single type
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1402.md
+dotnet_diagnostic.SA1402.severity = suggestion
+
+# SA1404: Code analysis suppression must have justification
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1404.md
+dotnet_diagnostic.SA1404.severity = suggestion
+
+# SA1411: Attribute constructor must not use unnecessary parenthesis
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1411.md
+dotnet_diagnostic.SA1411.severity = suggestion
+
+# SA1500: Braces for multiline statements must not share line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1500.md
+dotnet_diagnostic.SA1500.severity = suggestion
+
+# SA1501: Statement must not be on single line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1501.md
+dotnet_diagnostic.SA1501.severity = suggestion
+
+# SA1505: Opening braces must not be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1505.md
+dotnet_diagnostic.SA1505.severity = suggestion
+
+# SA1507: Code must not contain multiple blank lines in a row
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1507.md
+dotnet_diagnostic.SA1507.severity = suggestion
+
+# SA1508: Closing braces must not be preceded by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1508.md
+dotnet_diagnostic.SA1508.severity = suggestion
+
+# SA1512: Single line comments must not be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1512.md
+dotnet_diagnostic.SA1512.severity = suggestion
+
+# SA1513: Closing brace must be followed by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1513.md
+dotnet_diagnostic.SA1513.severity = suggestion
+
+# SA1515: Single line comment must be preceded by blank line
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1515.md
+dotnet_diagnostic.SA1515.severity = suggestion
+
+# SA1518: Use line endings correctly at end of file
+https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1518.md
+dotnet_diagnostic.SA1518.severity = suggestion
+
+# SA1636: File header copyright text must match
+https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1636.md
+dotnet_diagnostic.SA1636.severity = suggestion
+
+# SA1642: Constructor summary documentation must begin with standard text
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1642.md
+dotnet_diagnostic.SA1642.severity = suggestion
+
+# SA1649: File name must match type name
+https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1649.md
+dotnet_diagnostic.SA1649.severity = suggestion
+
+#### Meziantou.Analyzer rules ####
+
+# MA0001: StringComparison is missing
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0001.md
+dotnet_diagnostic.MA0001.severity = suggestion
+
+# MA0003: Add parameter name to improve readability
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0003.md
+dotnet_diagnostic.MA0003.severity = suggestion
+
+# MA0004: Use Task.ConfigureAwait(false)
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0004.md
+dotnet_diagnostic.MA0004.severity = suggestion
+
+# MA0005: Use Array.Empty()
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0005.md
+dotnet_diagnostic.MA0005.severity = suggestion
+
+# MA0011: IFormatProvider is missing
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0011.md
+dotnet_diagnostic.MA0011.severity = suggestion
+
+# MA0015: Specify the parameter name in ArgumentException
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0015.md
+dotnet_diagnostic.MA0015.severity = suggestion
+
+# MA0019: Use EventArgs.Empty
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0019.md
+dotnet_diagnostic.MA0019.severity = suggestion
+
+# MA0035: Do not use dangerous threading methods
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0035.md
+dotnet_diagnostic.MA0035.severity = suggestion
+
+# MA0040: Forward the CancellationToken parameter to methods that take one
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0040.md
+dotnet_diagnostic.MA0040.severity = suggestion
+
+# MA0046: Use EventHandler to declare events
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0046.md
+dotnet_diagnostic.MA0046.severity = suggestion
+
+# MA0053: Make class sealed
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md
+dotnet_diagnostic.MA0053.severity = suggestion
+
+# MA0060: The value returned by Stream.Read/Stream.ReadAsync is not used
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0060.md
+dotnet_diagnostic.MA0060.severity = suggestion
+
+# MA0075: Do not use implicit culture-sensitive ToString
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0075.md
+dotnet_diagnostic.MA0075.severity = suggestion
+
+# MA0099 - Use Explicit enum value instead of 0
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0099.md
+dotnet_diagnostic.MA0099.severity = suggestion
+
+# MA0110: Use the Regex source generator
+# https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0110.md
+dotnet_diagnostic.MA0110.severity = suggestion
+
#### .NET Compiler Platform analysers rules ####
+# CA1031: Do not catch general exception types
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1031
+dotnet_diagnostic.CA1031.severity = suggestion
+
+# CA1062: Validate arguments of public methods
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1062
+dotnet_diagnostic.CA1062.severity = suggestion
+
+# CA1063: Implement IDisposable correctly
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1063
+dotnet_diagnostic.CA1063.severity = suggestion
+
+# CA1307: Specify StringComparison for clarity
+https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1307
+dotnet_diagnostic.CA1307.severity = suggestion
+
+# CA1806: Do not ignore method results
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1806
+dotnet_diagnostic.CA1806.severity = suggestion
+
+# CA1812: Avoid uninstantiated internal classes
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1812
+dotnet_diagnostic.CA1812.severity = suggestion
+
+# CA1822: Mark members as static
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822
+dotnet_diagnostic.CA1822.severity = suggestion
+
+# CA1825: Avoid zero-length array allocations
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1825
+dotnet_diagnostic.CA1825.severity = suggestion
+
+# CA1859: Use concrete types when possible for improved performance
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1859
+dotnet_diagnostic.CA1859.severity = suggestion
+
+# CA2000: Dispose objects before losing scope
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2000
+dotnet_diagnostic.CA2000.severity = suggestion
+
+# CA2007: Do not directly await a Task
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2007
+dotnet_diagnostic.CA2007.severity = suggestion
+
+# CA2201: Do not raise reserved exception types
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2201
+dotnet_diagnostic.CA2201.severity = suggestion
+
+# CA2213: Disposable fields should be disposed
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2213
+dotnet_diagnostic.CA2213.severity = suggestion
+
+# CA2227: Collection properties should be read only
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
+dotnet_diagnostic.CA2227.severity = suggestion
+
# IDE0007: Use var instead of explicit type
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007
dotnet_diagnostic.IDE0007.severity = suggestion
diff --git a/test/Renci.SshNet.Tests/Classes/Common/SshDataTest.cs b/test/Renci.SshNet.Tests/Classes/Common/SshDataTest.cs
index e5b7bc86b..03b489121 100644
--- a/test/Renci.SshNet.Tests/Classes/Common/SshDataTest.cs
+++ b/test/Renci.SshNet.Tests/Classes/Common/SshDataTest.cs
@@ -1,5 +1,7 @@
using System;
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
+
using Renci.SshNet.Common;
namespace Renci.SshNet.Tests.Classes.Common
@@ -86,6 +88,55 @@ public void Load_DataAndOffsetAndCount()
Assert.AreEqual(two, request.ValueTwo);
}
+ [TestMethod]
+ public void ReadBytes_Length_LengthEqualsNumberOfBytesAvailable()
+ {
+ MySshData sshData = new MySshData();
+
+ sshData.Load(new byte[] { 0x01, 0x0d, 0x0a, 0x07, 0x0b }, 0, 4);
+ sshData.Write(new byte[] { 0x09, 0x03, 0x06, 0x0b }, 0, 3);
+
+ var bytes = sshData.ReadBytes(1);
+
+ CollectionAssert.AreEqual(new byte[] { 0x07 }, bytes);
+ }
+
+ [TestMethod]
+ public void ReadBytes_Length_LengthIsGreaterThanNumberOfBytesAvailable()
+ {
+ MySshData sshData = new MySshData();
+
+ sshData.Load(new byte[] { 0x01, 0x0d, 0x0a }, 0, 3);
+
+ try
+ {
+ sshData.ReadBytes(4);
+ Assert.Fail();
+ }
+ catch (ArgumentOutOfRangeException ex)
+ {
+ Assert.AreEqual(typeof(ArgumentOutOfRangeException), ex.GetType());
+ Assert.IsNull(ex.InnerException);
+ Assert.AreEqual("length", ex.ParamName);
+ }
+ }
+
+ [TestMethod]
+ public void ReadBytes_Length_LengthIsLessThanNumberOfBytesAvailable()
+ {
+ MySshData sshData = new MySshData();
+
+ sshData.Load(new byte[] { 0x05, 0x07, 0x02, 0x0b, 0x0a, 0x0d }, 0, 5);
+
+ var bytes = sshData.ReadBytes(2);
+
+ CollectionAssert.AreEqual(new byte[] { 0x05, 0x07 }, bytes);
+
+ bytes = sshData.ReadBytes(3);
+
+ CollectionAssert.AreEqual(new byte[] { 0x02, 0x0b, 0x0a }, bytes);
+ }
+
private class BoolSshData : SshData
{
private readonly bool _value;
@@ -145,5 +196,26 @@ protected override void SaveData()
Write(ValueTwo);
}
}
+
+ private class MySshData : SshData
+ {
+ protected override void LoadData()
+ {
+ }
+
+ protected override void SaveData()
+ {
+ }
+
+ public new byte[] ReadBytes(int length)
+ {
+ return base.ReadBytes(length);
+ }
+
+ public new void Write(byte[] buffer, int offset, int count)
+ {
+ base.Write(buffer, offset, count);
+ }
+ }
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessRead.cs
index 09423c6bd..e366914c7 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessRead.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessRead.cs
@@ -2,6 +2,7 @@
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -48,8 +49,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessReadWrite.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessReadWrite.cs
index 8f31995ce..d4b311ffa 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessReadWrite.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessReadWrite.cs
@@ -2,6 +2,7 @@
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -48,8 +49,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessRead.cs
index 47cabd2a6..5fd18e511 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessRead.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreateNew_FileAccessRead.cs
@@ -4,6 +4,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -50,8 +51,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessRead.cs
index 29e4956ac..c70b3239f 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessRead.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeCreate_FileAccessRead.cs
@@ -4,6 +4,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -50,8 +51,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessRead.cs
index 2ed977784..9e520a597 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessRead.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeTruncate_FileAccessRead.cs
@@ -2,6 +2,7 @@
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -48,8 +49,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs
index 14a2338b0..242755a04 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs
@@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -49,8 +50,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs
index c58f33873..776cec41f 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs
@@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -49,8 +50,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs
index 1d8b792dd..900f1b15d 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs
@@ -5,6 +5,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -51,8 +52,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs
index d253c30e4..6924da972 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs
@@ -5,6 +5,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -51,8 +52,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}
diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs
index 660bd1a1f..276455b7a 100644
--- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs
+++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs
@@ -5,6 +5,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
namespace Renci.SshNet.Tests.Classes.Sftp
{
@@ -51,8 +52,8 @@ public void CtorShouldHaveThrownArgumentException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
- Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException.Message);
- Assert.IsNull(_actualException.ParamName);
+ ArgumentExceptionAssert.MessageEquals(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", nameof(FileMode), _fileMode, nameof(FileAccess), _fileAccess), _actualException);
+ Assert.AreEqual("mode", _actualException.ParamName);
}
}
}