Skip to content

Commit 6af8a2f

Browse files
authored
Merge branch 'develop' into aesgcm-netframework
2 parents 08ba4cf + 484afbd commit 6af8a2f

File tree

11 files changed

+146
-133
lines changed

11 files changed

+146
-133
lines changed

.github/workflows/build.yml

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ jobs:
2525
- name: Build IntegrationTests .NET
2626
run: dotnet build -f net9.0 test/Renci.SshNet.IntegrationTests/
2727

28-
- name: Build IntegrationTests .NET Framework
29-
run: dotnet build -f net48 test/Renci.SshNet.IntegrationTests/
30-
3128
- name: Run Unit Tests .NET
3229
run: |
3330
dotnet test \
@@ -52,36 +49,14 @@ jobs:
5249
-p:CoverletOutput=../../coverlet/linux_integration_test_net_9_coverage.xml \
5350
test/Renci.SshNet.IntegrationTests/
5451
55-
# Also run a subset of the integration tests targeting netfx using mono. This is a temporary measure to get
56-
# some coverage until a proper solution for running the .NET Framework integration tests in CI is found.
57-
# Running all the tests causes problems which are not worth solving in this rare configuration.
58-
# See https://github.com/sshnet/SSH.NET/pull/1462 and related links
59-
- name: Run Integration Tests Mono
60-
run: |
61-
sudo apt-get install ca-certificates gnupg
62-
sudo gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mono-official-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
63-
echo "deb [signed-by=/usr/share/keyrings/mono-official-archive-keyring.gpg] https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
64-
sudo apt-get update
65-
sudo apt-get install mono-devel
66-
dotnet test \
67-
-f net48 \
68-
--no-build \
69-
--logger "console;verbosity=normal" \
70-
--logger GitHubActions \
71-
-p:CollectCoverage=true \
72-
-p:CoverletOutputFormat=cobertura \
73-
-p:CoverletOutput=../../coverlet/linux_integration_test_net_48_coverage.xml \
74-
--filter "Name~Ecdh|Name~ECDsa|Name~Zlib|Name~Gcm" \
75-
test/Renci.SshNet.IntegrationTests/
76-
7752
- name: Archive Coverlet Results
7853
uses: actions/upload-artifact@v4
7954
with:
8055
name: Coverlet Results Linux
8156
path: coverlet
8257

8358
Windows:
84-
runs-on: windows-2022
59+
runs-on: windows-2025
8560
steps:
8661
- name: Checkout
8762
uses: actions/checkout@v4
@@ -132,6 +107,44 @@ jobs:
132107
-p:CoverletOutput=../../coverlet/windows_unit_test_net_4_6_2_coverage.xml `
133108
test/Renci.SshNet.Tests/
134109
110+
Windows-Integration-Tests:
111+
name: Windows Integration Tests
112+
runs-on: windows-2025
113+
steps:
114+
- name: Checkout
115+
uses: actions/checkout@v4
116+
with:
117+
fetch-depth: 0 # needed for Nerdbank.GitVersioning
118+
119+
- name: Setup .NET
120+
uses: actions/setup-dotnet@v4
121+
with:
122+
dotnet-version: 9.0.x
123+
124+
- name: Setup WSL2
125+
uses: Vampire/setup-wsl@v5
126+
with:
127+
distribution: Ubuntu-24.04
128+
129+
- name: Setup SSH Server
130+
shell: wsl-bash {0}
131+
run: |
132+
apt-get update && apt-get upgrade -y
133+
apt-get install -y podman
134+
podman build -t renci-ssh-tests-server-image -f test/Renci.SshNet.IntegrationTests/Dockerfile test/Renci.SshNet.IntegrationTests/
135+
podman run --rm -h renci-ssh-tests-server -d -p 2222:22 renci-ssh-tests-server-image
136+
137+
- name: Run Integration Tests .NET Framework
138+
run:
139+
dotnet test `
140+
-f net48 `
141+
--logger "console;verbosity=normal" `
142+
--logger GitHubActions `
143+
-p:CollectCoverage=true `
144+
-p:CoverletOutputFormat=cobertura `
145+
-p:CoverletOutput=..\..\coverlet\windows_integration_test_net_4_8_coverage.xml `
146+
test\Renci.SshNet.IntegrationTests\
147+
135148
- name: Archive Coverlet Results
136149
uses: actions/upload-artifact@v4
137150
with:

src/Renci.SshNet/Common/ChannelInputStream.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,10 @@ public override int Read(byte[] buffer, int offset, int count)
101101
/// <exception cref="ArgumentOutOfRangeException">offset or count is negative.</exception>
102102
public override void Write(byte[] buffer, int offset, int count)
103103
{
104-
ThrowHelper.ThrowIfNull(buffer);
105-
106-
if (offset + count > buffer.Length)
107-
{
108-
throw new ArgumentException("The sum of offset and count is greater than the buffer length.");
109-
}
110-
111-
if (offset < 0 || count < 0)
112-
{
113-
throw new ArgumentOutOfRangeException(nameof(offset), "offset or count is negative.");
114-
}
104+
#if !NET
105+
ThrowHelper.
106+
#endif
107+
ValidateBufferArguments(buffer, offset, count);
115108

116109
ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
117110

src/Renci.SshNet/Common/PacketDump.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@ public static string Create(List<byte> data, int indentLevel)
1515
public static string Create(byte[] data, int indentLevel)
1616
{
1717
ThrowHelper.ThrowIfNull(data);
18-
19-
if (indentLevel < 0)
20-
{
21-
throw new ArgumentOutOfRangeException(nameof(indentLevel), "Cannot be less than zero.");
22-
}
18+
ThrowHelper.ThrowIfNegative(indentLevel);
2319

2420
const int lineWidth = 16;
2521

src/Renci.SshNet/Common/PipeStream.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ public override void SetLength(long value)
6464
/// <inheritdoc/>
6565
public override int Read(byte[] buffer, int offset, int count)
6666
{
67+
#if !NET
68+
ThrowHelper.
69+
#endif
70+
ValidateBufferArguments(buffer, offset, count);
71+
6772
lock (_sync)
6873
{
6974
while (_head == _tail && !_disposed)
@@ -88,6 +93,11 @@ public override int Read(byte[] buffer, int offset, int count)
8893
/// <inheritdoc/>
8994
public override void Write(byte[] buffer, int offset, int count)
9095
{
96+
#if !NET
97+
ThrowHelper.
98+
#endif
99+
ValidateBufferArguments(buffer, offset, count);
100+
91101
lock (_sync)
92102
{
93103
ThrowHelper.ThrowObjectDisposedIf(_disposed, this);

src/Renci.SshNet/Common/ThrowHelper.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,48 @@ static void Throw(string? argument, string? paramName)
7777
throw new ArgumentException("The value cannot be an empty string.", paramName);
7878
}
7979
}
80+
#endif
81+
}
82+
83+
#if !NET
84+
// A rough copy of
85+
// https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs#L960C13-L974C10
86+
// for lower targets.
87+
public static void ValidateBufferArguments(byte[] buffer, int offset, int count)
88+
{
89+
ThrowIfNull(buffer);
90+
ThrowIfNegative(offset);
91+
92+
if ((uint)count > buffer.Length - offset)
93+
{
94+
Throw();
95+
96+
[DoesNotReturn]
97+
static void Throw()
98+
{
99+
throw new ArgumentOutOfRangeException(
100+
nameof(count),
101+
"Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.");
102+
}
103+
}
104+
}
105+
#endif
106+
107+
public static void ThrowIfNegative(long value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
108+
{
109+
#if NET
110+
ArgumentOutOfRangeException.ThrowIfNegative(value, paramName);
111+
#else
112+
if (value < 0)
113+
{
114+
Throw(value, paramName);
115+
116+
[DoesNotReturn]
117+
static void Throw(long value, string? paramName)
118+
{
119+
throw new ArgumentOutOfRangeException(paramName, value, "Value must be non-negative.");
120+
}
121+
}
80122
#endif
81123
}
82124
}

src/Renci.SshNet/Security/KeyExchange.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ public byte[] ExchangeHash
6363
/// </summary>
6464
public event EventHandler<HostKeyEventArgs> HostKeyReceived;
6565

66-
private protected KeyExchange()
66+
/// <summary>
67+
/// Initializes a new instance of the <see cref="KeyExchange"/> class.
68+
/// </summary>
69+
protected KeyExchange()
6770
{
6871
_logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger(GetType());
6972
}

src/Renci.SshNet/Sftp/SftpFileStream.cs

Lines changed: 20 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -511,28 +511,12 @@ public override Task FlushAsync(CancellationToken cancellationToken)
511511
/// </remarks>
512512
public override int Read(byte[] buffer, int offset, int count)
513513
{
514-
var readLen = 0;
515-
516-
ThrowHelper.ThrowIfNull(buffer);
517-
518-
#if NET
519-
ArgumentOutOfRangeException.ThrowIfNegative(offset);
520-
ArgumentOutOfRangeException.ThrowIfNegative(count);
521-
#else
522-
if (offset < 0)
523-
{
524-
throw new ArgumentOutOfRangeException(nameof(offset));
525-
}
526-
527-
if (count < 0)
528-
{
529-
throw new ArgumentOutOfRangeException(nameof(count));
530-
}
514+
#if !NET
515+
ThrowHelper.
531516
#endif
532-
if ((buffer.Length - offset) < count)
533-
{
534-
throw new ArgumentException("Invalid array range.");
535-
}
517+
ValidateBufferArguments(buffer, offset, count);
518+
519+
var readLen = 0;
536520

537521
// Lock down the file stream while we do this.
538522
lock (_lock)
@@ -653,28 +637,14 @@ public override int Read(byte[] buffer, int offset, int count)
653637
/// <returns>A <see cref="Task" /> that represents the asynchronous read operation.</returns>
654638
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
655639
{
656-
var readLen = 0;
657-
658-
ThrowHelper.ThrowIfNull(buffer);
640+
#if !NET
641+
ThrowHelper.
642+
#endif
643+
ValidateBufferArguments(buffer, offset, count);
659644

660-
#if NET
661-
ArgumentOutOfRangeException.ThrowIfNegative(offset);
662-
ArgumentOutOfRangeException.ThrowIfNegative(count);
663-
#else
664-
if (offset < 0)
665-
{
666-
throw new ArgumentOutOfRangeException(nameof(offset));
667-
}
645+
cancellationToken.ThrowIfCancellationRequested();
668646

669-
if (count < 0)
670-
{
671-
throw new ArgumentOutOfRangeException(nameof(count));
672-
}
673-
#endif
674-
if ((buffer.Length - offset) < count)
675-
{
676-
throw new ArgumentException("Invalid array range.");
677-
}
647+
var readLen = 0;
678648

679649
CheckSessionIsOpen();
680650

@@ -952,14 +922,7 @@ public override long Seek(long offset, SeekOrigin origin)
952922
/// </remarks>
953923
public override void SetLength(long value)
954924
{
955-
#if NET
956-
ArgumentOutOfRangeException.ThrowIfNegative(value);
957-
#else
958-
if (value < 0)
959-
{
960-
throw new ArgumentOutOfRangeException(nameof(value));
961-
}
962-
#endif
925+
ThrowHelper.ThrowIfNegative(value);
963926

964927
// Lock down the file stream while we do this.
965928
lock (_lock)
@@ -1005,26 +968,10 @@ public override void SetLength(long value)
1005968
/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
1006969
public override void Write(byte[] buffer, int offset, int count)
1007970
{
1008-
ThrowHelper.ThrowIfNull(buffer);
1009-
1010-
#if NET
1011-
ArgumentOutOfRangeException.ThrowIfNegative(offset);
1012-
ArgumentOutOfRangeException.ThrowIfNegative(count);
1013-
#else
1014-
if (offset < 0)
1015-
{
1016-
throw new ArgumentOutOfRangeException(nameof(offset));
1017-
}
1018-
1019-
if (count < 0)
1020-
{
1021-
throw new ArgumentOutOfRangeException(nameof(count));
1022-
}
971+
#if !NET
972+
ThrowHelper.
1023973
#endif
1024-
if ((buffer.Length - offset) < count)
1025-
{
1026-
throw new ArgumentException("Invalid array range.");
1027-
}
974+
ValidateBufferArguments(buffer, offset, count);
1028975

1029976
// Lock down the file stream while we do this.
1030977
lock (_lock)
@@ -1105,26 +1052,12 @@ public override void Write(byte[] buffer, int offset, int count)
11051052
/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
11061053
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
11071054
{
1108-
ThrowHelper.ThrowIfNull(buffer);
1109-
1110-
#if NET
1111-
ArgumentOutOfRangeException.ThrowIfNegative(offset);
1112-
ArgumentOutOfRangeException.ThrowIfNegative(count);
1113-
#else
1114-
if (offset < 0)
1115-
{
1116-
throw new ArgumentOutOfRangeException(nameof(offset));
1117-
}
1118-
1119-
if (count < 0)
1120-
{
1121-
throw new ArgumentOutOfRangeException(nameof(count));
1122-
}
1055+
#if !NET
1056+
ThrowHelper.
11231057
#endif
1124-
if ((buffer.Length - offset) < count)
1125-
{
1126-
throw new ArgumentException("Invalid array range.");
1127-
}
1058+
ValidateBufferArguments(buffer, offset, count);
1059+
1060+
cancellationToken.ThrowIfCancellationRequested();
11281061

11291062
CheckSessionIsOpen();
11301063

src/Renci.SshNet/ShellStream.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,11 @@ public string Read()
789789
/// <inheritdoc/>
790790
public override int Read(byte[] buffer, int offset, int count)
791791
{
792+
#if !NET
793+
ThrowHelper.
794+
#endif
795+
ValidateBufferArguments(buffer, offset, count);
796+
792797
lock (_sync)
793798
{
794799
while (_readHead == _readTail && !_disposed)
@@ -835,6 +840,11 @@ public void Write(string? text)
835840
/// <inheritdoc/>
836841
public override void Write(byte[] buffer, int offset, int count)
837842
{
843+
#if !NET
844+
ThrowHelper.
845+
#endif
846+
ValidateBufferArguments(buffer, offset, count);
847+
838848
ThrowHelper.ThrowObjectDisposedIf(_disposed, this);
839849

840850
while (count > 0)

test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ public void Test_Sftp_Upload_Forbidden()
7777
[TestCategory("Sftp")]
7878
public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each()
7979
{
80+
if (Environment.GetEnvironmentVariable("CI") == "true")
81+
{
82+
Assert.Inconclusive("Skipping because of failures in CI, see #1253");
83+
}
84+
8085
var maxFiles = 10;
8186
var maxSize = 5;
8287

0 commit comments

Comments
 (0)