Skip to content

Add .NET 10 target #1672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,12 @@ dotnet_diagnostic.CA1848.severity = silent
# By default, this diagnostic is only reported for private members.
dotnet_code_quality.CA1859.api_surface = private,internal

# CA1873: Evaluation of this argument may be expensive and unnecessary if logging is disabled
dotnet_diagnostic.CA1873.severity = suggestion

# CA1508: Avoid dead conditional code. Too many false positives.
dotnet_diagnostic.CA1508.severity = suggestion

# CA2208: Instantiate argument exceptions correctly
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2208
#
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,33 @@ jobs:
uses: actions/setup-dotnet@v4

- name: Build Unit Tests .NET
run: dotnet build -f net9.0 test/Renci.SshNet.Tests/
run: dotnet build -f net10.0 test/Renci.SshNet.Tests/

- name: Build IntegrationTests .NET
run: dotnet build -f net9.0 test/Renci.SshNet.IntegrationTests/
run: dotnet build -f net10.0 test/Renci.SshNet.IntegrationTests/

- name: Run Unit Tests .NET
run: |
dotnet test \
-f net9.0 \
-f net10.0 \
--no-build \
--logger "console;verbosity=normal" \
--logger GitHubActions \
-p:CollectCoverage=true \
-p:CoverletOutputFormat=cobertura \
-p:CoverletOutput=../../coverlet/linux_unit_test_net_9_coverage.xml \
-p:CoverletOutput=../../coverlet/linux_unit_test_net_10_coverage.xml \
test/Renci.SshNet.Tests/

- name: Run Integration Tests .NET
run: |
dotnet test \
-f net9.0 \
-f net10.0 \
--no-build \
--logger "console;verbosity=normal" \
--logger GitHubActions \
-p:CollectCoverage=true \
-p:CoverletOutputFormat=cobertura \
-p:CoverletOutput=../../coverlet/linux_integration_test_net_9_coverage.xml \
-p:CoverletOutput=../../coverlet/linux_integration_test_net_10_coverage.xml \
test/Renci.SshNet.IntegrationTests/

- name: Archive Coverlet Results
Expand Down Expand Up @@ -82,13 +82,13 @@ jobs:
- name: Run Unit Tests .NET
run: |
dotnet test `
-f net9.0 `
-f net10.0 `
--no-build `
--logger "console;verbosity=normal" `
--logger GitHubActions `
-p:CollectCoverage=true `
-p:CoverletOutputFormat=cobertura `
-p:CoverletOutput=../../coverlet/windows_unit_test_net_9_coverage.xml `
-p:CoverletOutput=../../coverlet/windows_unit_test_net_10_coverage.xml `
test/Renci.SshNet.Tests/

- name: Run Unit Tests .NET Framework
Expand Down
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.210" />
<!-- Should stay on LTS .NET releases. -->
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.0-preview.6.25358.103" />
<PackageVersion Include="MSTest" Version="3.10.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.7.115" />
Expand All @@ -23,4 +23,4 @@
<PackageVersion Include="System.Formats.Asn1" Version="8.0.2" />
<PackageVersion Include="Testcontainers" Version="4.6.0" />
</ItemGroup>
</Project>
</Project>
3 changes: 2 additions & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"sdk": {
"version": "9.0.300",
"version": "10.0.100-preview.6.25358.103",
"allowPrerelease": true,
"rollForward": "latestFeature"
}
}
6 changes: 4 additions & 2 deletions src/Renci.SshNet/Common/Lock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ namespace Renci.SshNet.Common
{
internal sealed class Lock
{
private readonly object _lockObject = new object();

public bool TryEnter()
{
return Monitor.TryEnter(this);
return Monitor.TryEnter(_lockObject);
}

public void Exit()
{
Monitor.Exit(this);
Monitor.Exit(_lockObject);
}
}
}
Expand Down
7 changes: 2 additions & 5 deletions src/Renci.SshNet/NetConfClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,8 @@ protected override void Dispose(bool disposing)

if (disposing)
{
if (_netConfSession != null)
{
_netConfSession.Dispose();
_netConfSession = null;
}
_netConfSession?.Dispose();
_netConfSession = null;
}
}

Expand Down
14 changes: 4 additions & 10 deletions src/Renci.SshNet/Netconf/NetConfSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,11 @@ protected override void Dispose(bool disposing)

if (disposing)
{
if (_serverCapabilitiesConfirmed != null)
{
_serverCapabilitiesConfirmed.Dispose();
_serverCapabilitiesConfirmed = null;
}
_serverCapabilitiesConfirmed?.Dispose();
_serverCapabilitiesConfirmed = null;

if (_rpcReplyReceived != null)
{
_rpcReplyReceived.Dispose();
_rpcReplyReceived = null;
}
_rpcReplyReceived?.Dispose();
_rpcReplyReceived = null;
}
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/Renci.SshNet/PrivateKeyFile.OpenSSH.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public OpenSSH(byte[] data, string? passPhrase)
/// </summary>
public Key Parse()
{
var keyReader = new SshDataStream(_data);
using var keyReader = new SshDataStream(_data);

// check magic header
var authMagic = "openssh-key-v1\0"u8;
Expand Down Expand Up @@ -171,7 +171,7 @@ public Key Parse()
// now parse the data we called the private key, it actually contains the public key again
// so we need to parse through it to get the private key bytes, plus there's some
// validation we need to do.
var privateKeyReader = new SshDataStream(privateKeyBytes);
using var privateKeyReader = new SshDataStream(privateKeyBytes);

// check ints should match, they wouldn't match for example if the wrong passphrase was supplied
var checkInt1 = (int)privateKeyReader.ReadUInt32();
Expand Down Expand Up @@ -200,7 +200,9 @@ public Key Parse()

// k || ENC(A)
unencryptedPrivateKey = privateKeyReader.ReadBinary();
#pragma warning disable CA2000 // Dispose objects before losing scope
parsedKey = new ED25519Key(unencryptedPrivateKey);
#pragma warning restore CA2000 // Dispose objects before losing scope
break;
case "ecdsa-sha2-nistp256":
case "ecdsa-sha2-nistp384":
Expand All @@ -210,7 +212,9 @@ public Key Parse()
publicKey = privateKeyReader.ReadBinary();

unencryptedPrivateKey = privateKeyReader.ReadBinary();
#pragma warning disable CA2000 // Dispose objects before losing scope
parsedKey = new EcdsaKey(curve, publicKey, unencryptedPrivateKey.TrimLeadingZeros());
#pragma warning restore CA2000 // Dispose objects before losing scope
break;
case "ssh-rsa":
var modulus = privateKeyReader.ReadBigInt();
Expand All @@ -219,7 +223,9 @@ public Key Parse()
var inverseQ = privateKeyReader.ReadBigInt();
var p = privateKeyReader.ReadBigInt();
var q = privateKeyReader.ReadBigInt();
#pragma warning disable CA2000 // Dispose objects before losing scope
parsedKey = new RsaKey(modulus, exponent, d, p, q, inverseQ);
#pragma warning restore CA2000 // Dispose objects before losing scope
break;
default:
throw new SshException("OpenSSH key type '" + keyType + "' is not supported.");
Expand Down
4 changes: 2 additions & 2 deletions src/Renci.SshNet/PrivateKeyFile.PuTTY.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,11 @@ public Key Parse()
throw new SshException("MAC verification failed for PuTTY key file");
}

var publicKeyReader = new SshDataStream(_publicKey);
using var publicKeyReader = new SshDataStream(_publicKey);
var keyType = publicKeyReader.ReadString(Encoding.UTF8);
Debug.Assert(keyType == _algorithmName, $"{nameof(keyType)} is not the same as {nameof(_algorithmName)}");

var privateKeyReader = new SshDataStream(privateKey);
using var privateKeyReader = new SshDataStream(privateKey);

Key parsedKey;

Expand Down
34 changes: 17 additions & 17 deletions src/Renci.SshNet/PrivateKeyFile.SSHCOM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,22 @@ public SSHCOM(byte[] data, string? passPhrase)

public Key Parse()
{
var reader = new SshDataStream(_data);
var magicNumber = reader.ReadUInt32();
using var dataReader = new SshDataStream(_data);
var magicNumber = dataReader.ReadUInt32();
if (magicNumber != 0x3f6ff9eb)
{
throw new SshException("Invalid SSH2 private key.");
}

_ = reader.ReadUInt32(); // Read total bytes length including magic number
var keyType = reader.ReadString(SshData.Ascii);
var ssh2CipherName = reader.ReadString(SshData.Ascii);
var blobSize = (int)reader.ReadUInt32();
_ = dataReader.ReadUInt32(); // Read total bytes length including magic number
var keyType = dataReader.ReadString(SshData.Ascii);
var ssh2CipherName = dataReader.ReadString(SshData.Ascii);
var blobSize = (int)dataReader.ReadUInt32();

byte[] keyData;
if (ssh2CipherName == "none")
{
keyData = reader.ReadBytes(blobSize);
keyData = dataReader.ReadBytes(blobSize);
}
else if (ssh2CipherName == "3des-cbc")
{
Expand All @@ -53,17 +53,17 @@ public Key Parse()
}

var key = GetCipherKey(_passPhrase, 192 / 8);
var ssh2Сipher = new TripleDesCipher(key, new byte[8], CipherMode.CBC, pkcs7Padding: false);
keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
using var ssh2Сipher = new TripleDesCipher(key, new byte[8], CipherMode.CBC, pkcs7Padding: false);
keyData = ssh2Сipher.Decrypt(dataReader.ReadBytes(blobSize));
}
else
{
throw new SshException(string.Format("Cipher method '{0}' is not supported.", ssh2CipherName));
}

reader = new SshDataStream(keyData);
using var keyReader = new SshDataStream(keyData);

var decryptedLength = reader.ReadUInt32();
var decryptedLength = keyReader.ReadUInt32();

if (decryptedLength > blobSize - 4)
{
Expand All @@ -72,12 +72,12 @@ public Key Parse()

if (keyType.Contains("rsa"))
{
var exponent = ReadBigIntWithBits(reader);
var d = ReadBigIntWithBits(reader);
var modulus = ReadBigIntWithBits(reader);
var inverseQ = ReadBigIntWithBits(reader);
var q = ReadBigIntWithBits(reader);
var p = ReadBigIntWithBits(reader);
var exponent = ReadBigIntWithBits(keyReader);
var d = ReadBigIntWithBits(keyReader);
var modulus = ReadBigIntWithBits(keyReader);
var inverseQ = ReadBigIntWithBits(keyReader);
var q = ReadBigIntWithBits(keyReader);
var p = ReadBigIntWithBits(keyReader);
return new RsaKey(modulus, exponent, d, p, q, inverseQ);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Renci.SshNet/Renci.SshNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<AssemblyName>Renci.SshNet</AssemblyName>
<Product>SSH.NET</Product>
<AssemblyTitle>SSH.NET</AssemblyTitle>
<TargetFrameworks>net462;netstandard2.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net462;netstandard2.0;net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
Expand Down
14 changes: 4 additions & 10 deletions src/Renci.SshNet/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1570,17 +1570,11 @@ internal void OnNewKeysReceived(NewKeysMessage message)
disposableClientCipher.Dispose();
}

if (_serverMac != null)
{
_serverMac.Dispose();
_serverMac = null;
}
_serverMac?.Dispose();
_serverMac = null;

if (_clientMac != null)
{
_clientMac.Dispose();
_clientMac = null;
}
_clientMac?.Dispose();
_clientMac = null;

// Update negotiated algorithms
_serverCipher = _keyExchange.CreateServerCipher(out _serverAead);
Expand Down
2 changes: 2 additions & 0 deletions src/Renci.SshNet/Sftp/SftpFileStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,9 @@ private SftpFileStream(ISftpSession session, string path, FileAccess access, int

internal static SftpFileStream Open(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize)
{
#pragma warning disable CA2000 // Dispose objects before losing scope
return Open(session, path, mode, access, bufferSize, isAsync: false, CancellationToken.None).GetAwaiter().GetResult();
#pragma warning restore CA2000 // Dispose objects before losing scope
}

internal static Task<SftpFileStream> OpenAsync(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, CancellationToken cancellationToken)
Expand Down
6 changes: 6 additions & 0 deletions src/Renci.SshNet/SftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2305,6 +2305,8 @@ private List<FileInfo> InternalSynchronizeDirectories(string sourcePath, string
var remoteFileName = string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", destinationPath, localFile.Name);
try
{
#pragma warning disable CA2000 // Dispose objects before losing scope
#pragma warning disable CA2025 // Do not pass 'IDisposable' instances into unawaited tasks
using (var file = File.OpenRead(localFile.FullName))
{
InternalUploadFile(
Expand All @@ -2316,6 +2318,8 @@ private List<FileInfo> InternalSynchronizeDirectories(string sourcePath, string
isAsync: false,
CancellationToken.None).GetAwaiter().GetResult();
}
#pragma warning restore CA2025 // Do not pass 'IDisposable' instances into unawaited tasks
#pragma warning restore CA2000 // Dispose objects before losing scope

uploadedFiles.Add(localFile);

Expand Down Expand Up @@ -2482,6 +2486,7 @@ private async Task InternalDownloadFileAsync(string path, Stream output, Cancell
}

#pragma warning disable S6966 // Awaitable method should be used
#pragma warning disable CA1849 // Call async methods when in an async method
private async Task InternalUploadFile(
Stream input,
string path,
Expand Down Expand Up @@ -2621,6 +2626,7 @@ private async Task InternalUploadFile(
}
}
#pragma warning restore S6966 // Awaitable method should be used
#pragma warning restore CA1849 // Call async methods when in an async method

/// <summary>
/// Called when client is connected to the server.
Expand Down
7 changes: 2 additions & 5 deletions src/Renci.SshNet/SshClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,8 @@ protected override void Dispose(bool disposing)

if (disposing)
{
if (_inputStream != null)
{
_inputStream.Dispose();
_inputStream = null;
}
_inputStream?.Dispose();
_inputStream = null;

_isDisposed = true;
}
Expand Down
2 changes: 1 addition & 1 deletion test/Renci.SshNet.AotCompatibilityTestApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Renci.SshNet.AotCompatibilityTestApp
{
public static class Program
internal static class Program
{
public static void Main()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<PublishAot>true</PublishAot>
<SelfContained>true</SelfContained>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
Loading