Skip to content

Commit 4b9c3bf

Browse files
authored
Limit TimeSpan timeouts to Int32 MaxValue (#1321)
* Added guard clauses to various timeouts to ensure they don't exceed an Int32 in milliseconds. * Fixed guard clauses. * Updated build tags. * Added guard clauses to various timeouts to ensure they don't exceed an Int32 in milliseconds. * Fixed tests. * Added additional tests. * Replaced NoWarn with .editorconfig setting * Fixed references to parameter names.
1 parent 664595d commit 4b9c3bf

18 files changed

+328
-29
lines changed

src/Renci.SshNet/.editorconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,7 @@ dotnet_diagnostic.IDE0048.severity = none
159159
# IDE0305: Collection initialization can be simplified
160160
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0305
161161
dotnet_diagnostic.IDE0305.severity = none
162+
163+
# IDE0005: Remove unnecessary using directives
164+
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005
165+
dotnet_diagnostic.IDE0005.severity = suggestion

src/Renci.SshNet/Abstractions/SocketAbstraction.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public static void ClearReadBuffer(Socket socket)
129129

130130
public static int ReadPartial(Socket socket, byte[] buffer, int offset, int size, TimeSpan timeout)
131131
{
132-
socket.ReceiveTimeout = (int) timeout.TotalMilliseconds;
132+
socket.ReceiveTimeout = timeout.AsTimeout(nameof(timeout));
133133

134134
try
135135
{
@@ -274,7 +274,7 @@ public static int Read(Socket socket, byte[] buffer, int offset, int size, TimeS
274274
var totalBytesRead = 0;
275275
var totalBytesToRead = size;
276276

277-
socket.ReceiveTimeout = (int) readTimeout.TotalMilliseconds;
277+
socket.ReceiveTimeout = readTimeout.AsTimeout(nameof(readTimeout));
278278

279279
do
280280
{

src/Renci.SshNet/BaseClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public TimeSpan KeepAliveInterval
101101
{
102102
CheckDisposed();
103103

104+
value.EnsureValidTimeout(nameof(KeepAliveInterval));
105+
104106
if (value == _keepAliveInterval)
105107
{
106108
return;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
3+
namespace Renci.SshNet.Common
4+
{
5+
/// <summary>
6+
/// Provides extension methods for <see cref="TimeSpan"/>.
7+
/// </summary>
8+
internal static class TimeSpanExtensions
9+
{
10+
private const string OutOfRangeTimeoutMessage =
11+
$"The timeout must represent a value between -1 and Int32.MaxValue milliseconds, inclusive.";
12+
13+
/// <summary>
14+
/// Returns the specified <paramref name="timeSpan"/> as a valid timeout in milliseconds.
15+
/// </summary>
16+
/// <param name="timeSpan">The <see cref="TimeSpan"/> to ensure validity.</param>
17+
/// <param name="callerMemberName">The name of the calling member.</param>
18+
/// <exception cref="ArgumentOutOfRangeException">
19+
/// Thrown when <paramref name="timeSpan"/> does not represent a value between -1 and <see cref="int.MaxValue"/>, inclusive.
20+
/// </exception>
21+
public static int AsTimeout(this TimeSpan timeSpan, string callerMemberName)
22+
{
23+
var timeoutInMilliseconds = timeSpan.TotalMilliseconds;
24+
return timeoutInMilliseconds is < -1d or > int.MaxValue
25+
? throw new ArgumentOutOfRangeException(callerMemberName, OutOfRangeTimeoutMessage)
26+
: (int) timeoutInMilliseconds;
27+
}
28+
29+
/// <summary>
30+
/// Ensures that the specified <paramref name="timeSpan"/> represents a valid timeout in milliseconds.
31+
/// </summary>
32+
/// <param name="timeSpan">The <see cref="TimeSpan"/> to ensure validity.</param>
33+
/// <param name="callerMemberName">The name of the calling member.</param>
34+
/// <exception cref="ArgumentOutOfRangeException">
35+
/// Thrown when <paramref name="timeSpan"/> does not represent a value between -1 and <see cref="int.MaxValue"/>, inclusive.
36+
/// </exception>
37+
public static void EnsureValidTimeout(this TimeSpan timeSpan, string callerMemberName)
38+
{
39+
var timeoutInMilliseconds = timeSpan.TotalMilliseconds;
40+
if (timeoutInMilliseconds is < -1d or > int.MaxValue)
41+
{
42+
throw new ArgumentOutOfRangeException(callerMemberName, OutOfRangeTimeoutMessage);
43+
}
44+
}
45+
}
46+
}

src/Renci.SshNet/ConnectionInfo.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public class ConnectionInfo : IConnectionInfoInternal
4444
/// </value>
4545
private static readonly TimeSpan DefaultChannelCloseTimeout = TimeSpan.FromSeconds(1);
4646

47+
private TimeSpan _timeout;
48+
private TimeSpan _channelCloseTimeout;
49+
4750
/// <summary>
4851
/// Gets supported key exchange algorithms for this connection.
4952
/// </summary>
@@ -145,7 +148,19 @@ public class ConnectionInfo : IConnectionInfoInternal
145148
/// <value>
146149
/// The connection timeout. The default value is 30 seconds.
147150
/// </value>
148-
public TimeSpan Timeout { get; set; }
151+
public TimeSpan Timeout
152+
{
153+
get
154+
{
155+
return _timeout;
156+
}
157+
set
158+
{
159+
value.EnsureValidTimeout(nameof(Timeout));
160+
161+
_timeout = value;
162+
}
163+
}
149164

150165
/// <summary>
151166
/// Gets or sets the timeout to use when waiting for a server to acknowledge closing a channel.
@@ -157,7 +172,19 @@ public class ConnectionInfo : IConnectionInfoInternal
157172
/// If a server does not send a <c>SSH_MSG_CHANNEL_CLOSE</c> message before the specified timeout
158173
/// elapses, the channel will be closed immediately.
159174
/// </remarks>
160-
public TimeSpan ChannelCloseTimeout { get; set; }
175+
public TimeSpan ChannelCloseTimeout
176+
{
177+
get
178+
{
179+
return _channelCloseTimeout;
180+
}
181+
set
182+
{
183+
value.EnsureValidTimeout(nameof(ChannelCloseTimeout));
184+
185+
_channelCloseTimeout = value;
186+
}
187+
}
161188

162189
/// <summary>
163190
/// Gets or sets the character encoding.

src/Renci.SshNet/ForwardedPort.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ public void Dispose()
102102
/// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
103103
protected virtual void StopPort(TimeSpan timeout)
104104
{
105+
timeout.EnsureValidTimeout(nameof(timeout));
106+
105107
RaiseClosing();
106108

107109
var session = Session;

src/Renci.SshNet/ForwardedPortDynamic.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ protected override void StartPort()
101101
/// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
102102
protected override void StopPort(TimeSpan timeout)
103103
{
104+
timeout.EnsureValidTimeout(nameof(timeout));
105+
104106
if (!ForwardedPortStatus.ToStopping(ref _status))
105107
{
106108
return;

src/Renci.SshNet/ForwardedPortLocal.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ protected override void StartPort()
138138
/// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
139139
protected override void StopPort(TimeSpan timeout)
140140
{
141+
timeout.EnsureValidTimeout(nameof(timeout));
142+
141143
if (!ForwardedPortStatus.ToStopping(ref _status))
142144
{
143145
return;

src/Renci.SshNet/ForwardedPortRemote.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ protected override void StartPort()
188188
/// <param name="timeout">The maximum amount of time to wait for the port to stop.</param>
189189
protected override void StopPort(TimeSpan timeout)
190190
{
191+
timeout.EnsureValidTimeout(nameof(timeout));
192+
191193
if (!ForwardedPortStatus.ToStopping(ref _status))
192194
{
193195
return;

src/Renci.SshNet/NetConfClient.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,7 @@ public TimeSpan OperationTimeout
3636
}
3737
set
3838
{
39-
var timeoutInMilliseconds = value.TotalMilliseconds;
40-
if (timeoutInMilliseconds is < -1d or > int.MaxValue)
41-
{
42-
throw new ArgumentOutOfRangeException(nameof(value), "The timeout must represent a value between -1 and Int32.MaxValue, inclusive.");
43-
}
44-
45-
_operationTimeout = (int) timeoutInMilliseconds;
39+
_operationTimeout = value.AsTimeout(nameof(OperationTimeout));
4640
}
4741
}
4842

0 commit comments

Comments
 (0)