@@ -829,7 +829,7 @@ internal void SendMessage(Message message)
829
829
// atomically, and only after the packet has actually been sent
830
830
lock ( _socketWriteLock )
831
831
{
832
- if ( _socket == null || ! _socket . Connected )
832
+ if ( ! _socket . IsConnected ( ) )
833
833
throw new SshConnectionException ( "Client not connected." ) ;
834
834
835
835
byte [ ] hash = null ;
@@ -914,9 +914,8 @@ private bool TrySendMessage(Message message)
914
914
/// Receives the message from the server.
915
915
/// </summary>
916
916
/// <returns>
917
- /// The incoming SSH message.
917
+ /// The incoming SSH message, or <c>null</c> if the connection with the SSH server was closed .
918
918
/// </returns>
919
- /// <exception cref="SshConnectionException"></exception>
920
919
/// <remarks>
921
920
/// We need no locking here since all messages are read by a single thread.
922
921
/// </remarks>
@@ -945,7 +944,12 @@ private Message ReceiveMessage()
945
944
{
946
945
#endif // FEATURE_SOCKET_POLL
947
946
// Read first block - which starts with the packet length
948
- var firstBlock = SocketRead ( blockSize ) ;
947
+ var firstBlock = new byte [ blockSize ] ;
948
+ if ( TrySocketRead ( firstBlock , 0 , blockSize ) == 0 )
949
+ {
950
+ // connection with SSH server was closed
951
+ return null ;
952
+ }
949
953
950
954
#if DEBUG_GERT
951
955
DiagnosticAbstraction . Log ( string . Format ( "[{0}] FirstBlock [{1}]: {2}" , ToHex ( SessionId ) , blockSize , ToHex ( firstBlock ) ) ) ;
@@ -988,7 +992,10 @@ private Message ReceiveMessage()
988
992
989
993
if ( bytesToRead > 0 )
990
994
{
991
- SocketRead ( data , blockSize + inboundPacketSequenceLength , bytesToRead ) ;
995
+ if ( TrySocketRead ( data , blockSize + inboundPacketSequenceLength , bytesToRead ) == 0 )
996
+ {
997
+ return null ;
998
+ }
992
999
}
993
1000
#if FEATURE_SOCKET_POLL
994
1001
}
@@ -1787,20 +1794,6 @@ private void SocketConnect(string host, int port)
1787
1794
_socket . ReceiveBufferSize = socketBufferSize ;
1788
1795
}
1789
1796
1790
- /// <summary>
1791
- /// Performs a blocking read on the socket until <paramref name="length"/> bytes are received.
1792
- /// </summary>
1793
- /// <param name="length">The number of bytes to read.</param>
1794
- /// <returns>
1795
- /// The bytes read from the server.
1796
- /// </returns>
1797
- private byte [ ] SocketRead ( int length )
1798
- {
1799
- var buffer = new byte [ length ] ;
1800
- SocketRead ( buffer , 0 , length ) ;
1801
- return buffer ;
1802
- }
1803
-
1804
1797
/// <summary>
1805
1798
/// Performs a blocking read on the socket until <paramref name="length"/> bytes are received.
1806
1799
/// </summary>
@@ -1827,6 +1820,22 @@ private int SocketRead(byte[] buffer, int offset, int length)
1827
1820
return bytesRead ;
1828
1821
}
1829
1822
1823
+ /// <summary>
1824
+ /// Performs a blocking read on the socket until <paramref name="length"/> bytes are received.
1825
+ /// </summary>
1826
+ /// <param name="buffer">An array of type <see cref="byte"/> that is the storage location for the received data.</param>
1827
+ /// <param name="offset">The position in <paramref name="buffer"/> parameter to store the received data.</param>
1828
+ /// <param name="length">The number of bytes to read.</param>
1829
+ /// <returns>
1830
+ /// The number of bytes read.
1831
+ /// </returns>
1832
+ /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception>
1833
+ /// <exception cref="SocketException">The read failed.</exception>
1834
+ private int TrySocketRead ( byte [ ] buffer , int offset , int length )
1835
+ {
1836
+ return SocketAbstraction . Read ( _socket , buffer , offset , length , InfiniteTimeSpan ) ;
1837
+ }
1838
+
1830
1839
/// <summary>
1831
1840
/// Performs a blocking read on the socket until a line is read.
1832
1841
/// </summary>
@@ -1920,26 +1929,56 @@ private void MessageListener()
1920
1929
var readSockets = new List < Socket > { _socket } ;
1921
1930
1922
1931
// remain in message loop until socket is shut down or until we're disconnecting
1923
- while ( true )
1932
+ while ( _socket . IsConnected ( ) )
1924
1933
{
1925
1934
#if FEATURE_SOCKET_POLL
1935
+ // if the socket is already disposed when Select is invoked, then a SocketException
1936
+ // stating "An operation was attempted on something that is not a socket" is thrown;
1937
+ // we attempt to avoid this exception by having an IsConnected() that can break the
1938
+ // message loop
1939
+ //
1940
+ // note that there's no guarantee that the socket will not be disposed between the
1941
+ // IsConnected() check and the Select invocation; we can't take a "dispose" lock
1942
+ // that includes the Select invocation as we want Dispose() to be able to interrupt
1943
+ // the Select
1944
+
1945
+ // perform a blocking select to determine whether there's is data available to be
1946
+ // read; we do not use a blocking read to allow us to use Socket.Poll to determine
1947
+ // if the connection is still available (in IsSocketConnected
1926
1948
Socket . Select ( readSockets , null , null , - 1 ) ;
1927
1949
1928
- if ( readSockets . Count == 0 )
1950
+ // the Select invocation will be interrupted in one of the following conditions:
1951
+ // * data is available to be read
1952
+ // => the socket will not be removed from "readSockets"
1953
+ // * the socket connection is closed during the Select invocation
1954
+ // => the socket will be removed from "readSockets"
1955
+ // * the socket is disposed during the Select invocation
1956
+ // => the socket will not be removed from "readSocket"
1957
+ //
1958
+ // since we handle the second and third condition the same way and Socket.Connected
1959
+ // allows us to check for both conditions, we use that instead of both checking for
1960
+ // the removal from "readSockets" and the Connection check
1961
+ if ( ! _socket . IsConnected ( ) )
1962
+ {
1963
+ // connection with SSH server was closed or socket was disposed;
1964
+ // break out of the message loop
1929
1965
break ;
1930
-
1931
- // when the socket is disposed while a Select is executing, then the
1932
- // Select will be interrupted; the socket will not be removed from
1933
- // readSocket, and therefore we need to explicitly check if the
1934
- // socket is still connected
1966
+ }
1935
1967
#endif // FEATURE_SOCKET_POLL
1936
- var socket = _socket ;
1937
- if ( socket == null || ! socket . Connected )
1938
- break ;
1939
1968
1940
1969
var message = ReceiveMessage ( ) ;
1970
+ if ( message == null )
1971
+ {
1972
+ // connection with SSH server was closed;
1973
+ // break out of the message loop
1974
+ break ;
1975
+ }
1976
+
1941
1977
HandleMessageCore ( message ) ;
1942
1978
}
1979
+
1980
+ // connection with SSH server was closed
1981
+ RaiseError ( CreateConnectionAbortedByServerException ( ) ) ;
1943
1982
}
1944
1983
catch ( SocketException ex )
1945
1984
{
@@ -2305,7 +2344,13 @@ private void Reset()
2305
2344
_keyExchangeInProgress = false ;
2306
2345
}
2307
2346
2308
- #region IDisposable implementation
2347
+ private static SshConnectionException CreateConnectionAbortedByServerException ( )
2348
+ {
2349
+ return new SshConnectionException ( "An established connection was aborted by the server." ,
2350
+ DisconnectReason . ConnectionLost ) ;
2351
+ }
2352
+
2353
+ #region IDisposable implementation
2309
2354
2310
2355
private bool _disposed ;
2311
2356
@@ -2396,9 +2441,9 @@ protected virtual void Dispose(bool disposing)
2396
2441
Dispose ( false ) ;
2397
2442
}
2398
2443
2399
- #endregion IDisposable implementation
2444
+ #endregion IDisposable implementation
2400
2445
2401
- #region ISession implementation
2446
+ #region ISession implementation
2402
2447
2403
2448
/// <summary>
2404
2449
/// Gets or sets the connection info.
@@ -2483,6 +2528,6 @@ bool ISession.TrySendMessage(Message message)
2483
2528
return TrySendMessage ( message ) ;
2484
2529
}
2485
2530
2486
- #endregion ISession implementation
2531
+ #endregion ISession implementation
2487
2532
}
2488
2533
}
0 commit comments