Skip to content

Commit 34e4285

Browse files
committed
Added guard clauses to various timeouts to ensure they don't exceed an Int32 in milliseconds.
1 parent d07827b commit 34e4285

13 files changed

+138
-21
lines changed

src/Renci.SshNet/Abstractions/SocketAbstraction.cs

+2-2
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();
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();
278278

279279
do
280280
{

src/Renci.SshNet/BaseClient.cs

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public TimeSpan KeepAliveInterval
101101
{
102102
CheckDisposed();
103103

104+
value.EnsureValidTimeout();
105+
104106
if (value == _keepAliveInterval)
105107
{
106108
return;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
#if NET21_OR_GREATER
3+
using System.Runtime.CompilerServices;
4+
#endif
5+
6+
namespace Renci.SshNet.Common
7+
{
8+
/// <summary>
9+
/// Provides extension methods for <see cref="TimeSpan"/>.
10+
/// </summary>
11+
internal static class TimeSpanExtensions
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,
22+
#if NET21_OR_GREATER
23+
[CallerArgumentExpression(nameof(timeSpan))]
24+
#endif
25+
string callerMemberName = "")
26+
{
27+
var timeoutInMilliseconds = timeSpan.TotalMilliseconds;
28+
return timeoutInMilliseconds is < -1d or > int.MaxValue
29+
? throw new ArgumentOutOfRangeException(callerMemberName, "The timeout must represent a value between -1 and Int32.MaxValue, inclusive.")
30+
: (int) timeoutInMilliseconds;
31+
}
32+
33+
/// <summary>
34+
/// Ensures that the specified <paramref name="timeSpan"/> represents a valid timeout in milliseconds.
35+
/// </summary>
36+
/// <param name="timeSpan">The <see cref="TimeSpan"/> to ensure validity.</param>
37+
/// <param name="callerMemberName">The name of the calling member.</param>
38+
/// <exception cref="ArgumentOutOfRangeException">
39+
/// Thrown when <paramref name="timeSpan"/> does not represent a value between -1 and <see cref="int.MaxValue"/>, inclusive.
40+
/// </exception>
41+
public static void EnsureValidTimeout(this TimeSpan timeSpan,
42+
#if NET21_OR_GREATER
43+
[CallerArgumentExpression(nameof(timeSpan))]
44+
#endif
45+
string callerMemberName = "")
46+
{
47+
var timeoutInMilliseconds = timeSpan.TotalMilliseconds;
48+
if (timeoutInMilliseconds is < -1d or > int.MaxValue)
49+
{
50+
throw new ArgumentOutOfRangeException(callerMemberName, "The timeout must represent a value between -1 and Int32.MaxValue, inclusive.");
51+
}
52+
}
53+
}
54+
}

src/Renci.SshNet/ConnectionInfo.cs

+28-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public class ConnectionInfo : IConnectionInfoInternal
4343
/// 1 second.
4444
/// </value>
4545
private static readonly TimeSpan DefaultChannelCloseTimeout = TimeSpan.FromSeconds(1);
46+
private TimeSpan _timeout;
47+
private TimeSpan _channelCloseTimeout;
4648

4749
/// <summary>
4850
/// Gets supported key exchange algorithms for this connection.
@@ -145,7 +147,19 @@ public class ConnectionInfo : IConnectionInfoInternal
145147
/// <value>
146148
/// The connection timeout. The default value is 30 seconds.
147149
/// </value>
148-
public TimeSpan Timeout { get; set; }
150+
public TimeSpan Timeout
151+
{
152+
get
153+
{
154+
return _timeout;
155+
}
156+
set
157+
{
158+
value.EnsureValidTimeout();
159+
160+
_timeout = value;
161+
}
162+
}
149163

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

162188
/// <summary>
163189
/// Gets or sets the character encoding.

src/Renci.SshNet/ForwardedPort.cs

+2
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();
106+
105107
RaiseClosing();
106108

107109
var session = Session;

src/Renci.SshNet/ForwardedPortDynamic.cs

+2
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();
105+
104106
if (!ForwardedPortStatus.ToStopping(ref _status))
105107
{
106108
return;

src/Renci.SshNet/ForwardedPortLocal.cs

+2
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();
142+
141143
if (!ForwardedPortStatus.ToStopping(ref _status))
142144
{
143145
return;

src/Renci.SshNet/ForwardedPortRemote.cs

+2
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();
192+
191193
if (!ForwardedPortStatus.ToStopping(ref _status))
192194
{
193195
return;

src/Renci.SshNet/NetConfClient.cs

+1-7
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();
4640
}
4741
}
4842

src/Renci.SshNet/ScpClient.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public partial class ScpClient : BaseClient
3838
private static readonly Regex TimestampRe = new Regex(@"T(?<mtime>\d+) 0 (?<atime>\d+) 0", RegexOptions.Compiled);
3939

4040
private IRemotePathTransformation _remotePathTransformation;
41+
private TimeSpan _operationTimeout;
4142

4243
/// <summary>
4344
/// Gets or sets the operation timeout.
@@ -46,7 +47,19 @@ public partial class ScpClient : BaseClient
4647
/// The timeout to wait until an operation completes. The default value is negative
4748
/// one (-1) milliseconds, which indicates an infinite time-out period.
4849
/// </value>
49-
public TimeSpan OperationTimeout { get; set; }
50+
public TimeSpan OperationTimeout
51+
{
52+
get
53+
{
54+
return _operationTimeout;
55+
}
56+
set
57+
{
58+
value.EnsureValidTimeout();
59+
60+
_operationTimeout = value;
61+
}
62+
}
5063

5164
/// <summary>
5265
/// Gets or sets the size of the buffer.

src/Renci.SshNet/Sftp/SftpFileStream.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class SftpFileStream : Stream
3535
private bool _canRead;
3636
private bool _canSeek;
3737
private bool _canWrite;
38+
private TimeSpan _timeout;
3839

3940
/// <summary>
4041
/// Gets a value indicating whether the current stream supports reading.
@@ -176,7 +177,19 @@ public virtual byte[] Handle
176177
/// <value>
177178
/// The timeout.
178179
/// </value>
179-
public TimeSpan Timeout { get; set; }
180+
public TimeSpan Timeout
181+
{
182+
get
183+
{
184+
return _timeout;
185+
}
186+
set
187+
{
188+
_timeout.EnsureValidTimeout();
189+
190+
_timeout = value;
191+
}
192+
}
180193

181194
private SftpFileStream(ISftpSession session, string path, FileAccess access, int bufferSize, byte[] handle, long position)
182195
{

src/Renci.SshNet/SftpClient.cs

+1-7
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,7 @@ public TimeSpan OperationTimeout
5959
{
6060
CheckDisposed();
6161

62-
var timeoutInMilliseconds = value.TotalMilliseconds;
63-
if (timeoutInMilliseconds is < -1d or > int.MaxValue)
64-
{
65-
throw new ArgumentOutOfRangeException(nameof(value), "The timeout must represent a value between -1 and Int32.MaxValue, inclusive.");
66-
}
67-
68-
_operationTimeout = (int) timeoutInMilliseconds;
62+
_operationTimeout = value.AsTimeout();
6963
}
7064
}
7165

src/Renci.SshNet/SshCommand.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class SshCommand : IDisposable
3232
private bool _hasError;
3333
private bool _isDisposed;
3434
private ChannelInputStream _inputStream;
35+
private TimeSpan _commandTimeout;
3536

3637
/// <summary>
3738
/// Gets the command text.
@@ -44,7 +45,19 @@ public class SshCommand : IDisposable
4445
/// <value>
4546
/// The command timeout.
4647
/// </value>
47-
public TimeSpan CommandTimeout { get; set; }
48+
public TimeSpan CommandTimeout
49+
{
50+
get
51+
{
52+
return _commandTimeout;
53+
}
54+
set
55+
{
56+
_commandTimeout.EnsureValidTimeout();
57+
58+
_commandTimeout = value;
59+
}
60+
}
4861

4962
/// <summary>
5063
/// Gets the command exit status.

0 commit comments

Comments
 (0)