فهرست منبع

Do not allow simultaneously sending of a packet and disposing of the session.
Fixes variant of issue #32.

Impact on performance still needs to be checked, so we may revert this later.

drieseng 9 سال پیش
والد
کامیت
f50b9f18a6
1فایلهای تغییر یافته به همراه49 افزوده شده و 25 حذف شده
  1. 49 25
      src/Renci.SshNet/Session.cs

+ 49 - 25
src/Renci.SshNet/Session.cs

@@ -300,20 +300,20 @@ namespace Renci.SshNet
                 if (_clientInitMessage == null)
                 {
                     _clientInitMessage = new KeyExchangeInitMessage
-                    {
-                        KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(),
-                        ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(),
-                        EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(),
-                        EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(),
-                        MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(),
-                        MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(),
-                        CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(),
-                        CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(),
-                        LanguagesClientToServer = new[] {string.Empty},
-                        LanguagesServerToClient = new[] {string.Empty},
-                        FirstKexPacketFollows = false,
-                        Reserved = 0
-                    };
+                        {
+                            KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(),
+                            ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(),
+                            EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(),
+                            EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(),
+                            MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(),
+                            MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(),
+                            CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(),
+                            CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(),
+                            LanguagesClientToServer = new[] {string.Empty},
+                            LanguagesServerToClient = new[] {string.Empty},
+                            FirstKexPacketFollows = false,
+                            Reserved = 0
+                        };
                 }
                 return _clientInitMessage;
             }
@@ -798,11 +798,11 @@ namespace Renci.SshNet
                 throw new ArgumentNullException("waitHandle");
 
             var waitHandles = new[]
-            {
-                _exceptionWaitHandle,
-                _messageListenerCompleted,
-                waitHandle
-            };
+                {
+                    _exceptionWaitHandle,
+                    _messageListenerCompleted,
+                    waitHandle
+                };
 
             switch (WaitHandle.WaitAny(waitHandles, timeout))
             {
@@ -852,9 +852,6 @@ namespace Renci.SshNet
             // atomically, and only after the packet has actually been sent
             lock (_socketWriteLock)
             {
-                if (!_socket.IsConnected())
-                    throw new SshConnectionException("Client not connected.");
-
                 byte[] hash = null;
                 var packetDataOffset = 4; // first four bytes are reserved for outbound packet sequence
 
@@ -881,15 +878,14 @@ namespace Renci.SshNet
                 var packetLength = packetData.Length - packetDataOffset;
                 if (hash == null)
                 {
-                    SocketAbstraction.Send(_socket, packetData, packetDataOffset, packetLength);
+                    SendPacket(packetData, packetDataOffset, packetLength);
                 }
                 else
                 {
                     var data = new byte[packetLength + (_clientMac.HashSize/8)];
                     Buffer.BlockCopy(packetData, packetDataOffset, data, 0, packetLength);
                     Buffer.BlockCopy(hash, 0, data, packetLength, hash.Length);
-
-                    SocketAbstraction.Send(_socket, data, 0, data.Length);
+                    SendPacket(data, 0, data.Length);
                 }
 
                 // increment the packet sequence number only after we're sure the packet has
@@ -902,6 +898,34 @@ namespace Renci.SshNet
             }
         }
 
+        /// <summary>
+        /// Sends an SSH packet to the server.
+        /// </summary>
+        /// <param name="packet">A byte array containing the packet to send.</param>
+        /// <param name="offset">The offset of the packet.</param>
+        /// <param name="length">The length of the packet.</param>
+        /// <exception cref="SshConnectionException">Client is not connected to the server.</exception>
+        /// <remarks>
+        /// <para>
+        /// The send is performed in a dispose lock to avoid <see cref="NullReferenceException"/>
+        /// and/or <see cref="ObjectDisposedException"/> when sending the packet.
+        /// </para>
+        /// <para>
+        /// This method is only to be used when the connection is established, as the locking
+        /// overhead is not required while establising the connection.
+        /// </para>
+        /// </remarks>
+        private void SendPacket(byte[] packet, int offset, int length)
+        {
+            lock (_socketDisposeLock)
+            {
+                if (!_socket.IsConnected())
+                    throw new SshConnectionException("Client not connected.");
+
+                SocketAbstraction.Send(_socket, packet, offset, length);
+            }
+        }
+
         /// <summary>
         /// Sends a message to the server.
         /// </summary>