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 7 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
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,9 @@ dotnet_diagnostic.CA1848.severity = suggestion
# 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

# 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.205" />
<!-- 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.9.3" />
<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"
}
}
4 changes: 4 additions & 0 deletions src/Renci.SshNet/Channels/ChannelDirectTcpip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,12 @@ private void CloseSocket()

lock (_socketLock)
{
#pragma warning disable CA1508 // Avoid dead conditional code
if (_socket is null)
{
return;
}
#pragma warning restore CA1508 // Avoid dead conditional code

// closing a socket actually disposes the socket, so we can safely dereference
// the field to avoid entering the lock again later
Expand Down Expand Up @@ -293,11 +295,13 @@ protected override void Dispose(bool disposing)
lock (_socketLock)
{
var socket = _socket;
#pragma warning disable CA1508 // Avoid dead conditional code
if (socket != null)
{
_socket = null;
socket.Dispose();
}
#pragma warning restore CA1508 // Avoid dead conditional code
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,13 @@ private void CloseSocket()
lock (_socketShutdownAndCloseLock)
{
var socket = _socket;
#pragma warning disable CA1508 // Avoid dead conditional code
if (socket != null)
{
_socket = null;
socket.Dispose();
}
#pragma warning restore CA1508 // Avoid dead conditional code
}
}

Expand Down
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 @@ -1561,17 +1561,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 @@ -1161,6 +1161,7 @@ protected override void Dispose(bool disposing)
{
lock (_lock)
{
#pragma warning disable CA1508 // Avoid dead conditional code
if (_session != null)
{
_canRead = false;
Expand All @@ -1184,6 +1185,7 @@ protected override void Dispose(bool disposing)

_session = null;
}
#pragma warning restore CA1508 // Avoid dead conditional code
}
}
}
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
Loading