diff --git a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs index 0f5844d7..76ec691e 100644 --- a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs +++ b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs @@ -259,12 +259,12 @@ namespace Renci.SshClient.Channels this.LocalWindowSize -= (uint)messageData.Length; // Adjust window if window size is too low - if (this.LocalWindowSize < 1) + if (this.LocalWindowSize < this.PacketSize) { this.SendMessage(new ChannelWindowAdjustMessage { ChannelNumber = this.ServerChannelNumber, - BytesToAdd = this._initialWindowSize, + BytesToAdd = this._initialWindowSize - this.LocalWindowSize, }); this.LocalWindowSize = this._initialWindowSize; } diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs index d47a6630..8da7d818 100644 --- a/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs +++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs @@ -26,7 +26,7 @@ namespace Renci.SshClient.Channels } public ChannelExec(Session session, uint channelId) - : base(session, channelId, 0x010000, 0x8000) + : base(session, channelId, 0x100000, 0x8000) { } diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs index 7b249d6b..fe005163 100644 --- a/Renci.SshClient/Renci.SshClient/Session.cs +++ b/Renci.SshClient/Renci.SshClient/Session.cs @@ -60,27 +60,27 @@ namespace Renci.SshClient /// /// WaitHandle to signal that key exchange was finished, weither it was succesfull or not. /// - private EventWaitHandle _keyExhangedFinishedWaitHandle = new AutoResetEvent(false); + private EventWaitHandle _keyExhangedFinishedWaitHandle; /// /// WaitHandle to signale that last service request was accepted /// - private EventWaitHandle _serviceAccepted = new AutoResetEvent(false); + private EventWaitHandle _serviceAccepted; /// /// WaitHandle to signal that exception was thrown by another thread. /// - private EventWaitHandle _exceptionWaitHandle = new AutoResetEvent(false); + private EventWaitHandle _exceptionWaitHandle; /// /// WaitHandle to signal that listner ended /// - private EventWaitHandle _listenerWaitHandle = new AutoResetEvent(false); + private EventWaitHandle _listenerWaitHandle; /// /// Keeps track of all open channels /// - private Dictionary _openChannels = new Dictionary(); + private Dictionary _openChannels; /// /// Exception that need to be thrown by waiting thread @@ -100,7 +100,7 @@ namespace Renci.SshClient /// /// holds number to be used for session channels /// - private BlockingStack _channelNumbers = new BlockingStack(); + private BlockingStack _channelNumbers; /// /// Occurs when new message received. @@ -182,12 +182,6 @@ namespace Renci.SshClient { this.ConnectionInfo = connectionInfo; this.ClientVersion = string.Format("SSH-2.0-Renci.SshClient.{0}", this.GetType().Assembly.GetName().Version); - - // Prepopulate chanel numbers that will be used - for (int i = connectionInfo.MaxSessions - 1; i > 0; i--) - { - this._channelNumbers.Push((uint)i - 1 + 100); - } } private static IDictionary> _channels = new Dictionary>() @@ -256,6 +250,15 @@ namespace Renci.SshClient if (this.IsConnected) return; + this.Initialize(); + + // Prepopulate chanel numbers that will be used + for (int i = connectionInfo.MaxSessions - 1; i > 0; i--) + { + this._channelNumbers.Push((uint)i - 1); + } + + var ep = new IPEndPoint(Dns.GetHostAddresses(connectionInfo.Host)[0], connectionInfo.Port); this._socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); @@ -513,19 +516,13 @@ namespace Renci.SshClient // Read rest of the packet data int bytesToRead = (int)(packetLength - (blockSize - 4)); - while (bytesToRead > 0) + if (this.ServerCipher == null) { - data = new List(this.Read(blockSize)); - - if (this.ServerCipher == null) - { - decryptedData.AddRange(data); - } - else - { - decryptedData.AddRange(this.ServerCipher.Decrypt(data)); - } - bytesToRead -= blockSize; + decryptedData.AddRange(this.Read(bytesToRead)); + } + else + { + decryptedData.AddRange(this.ServerCipher.Decrypt(this.Read(bytesToRead))); } // Validate message against MAC @@ -704,29 +701,28 @@ namespace Renci.SshClient protected byte[] Read(int length) { var buffer = new byte[length]; - - var totalBytesRead = 0; - - while (totalBytesRead < length) + var offset = 0; + int received = 0; // how many bytes is already received + do { - int bytesRead = this._socket.Receive(buffer, length, SocketFlags.None); + try + { + received += this._socket.Receive(buffer, offset + received, length - received, SocketFlags.None); + } + catch (SocketException exp) + { + if (exp.SocketErrorCode == SocketError.WouldBlock || + exp.SocketErrorCode == SocketError.IOPending || + exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable) + { + // socket buffer is probably empty, wait and try again + Thread.Sleep(30); + } + else + throw exp; // any serious error occurr + } + } while (received < length); - if (bytesRead == 0) - { - this._socket.Close(); - throw new IOException("Unable to read data to the transport connection: An established connection was aborted by the software in your host machine"); - } - - if (bytesRead > 0) - { - totalBytesRead += bytesRead; - } - else - { - totalBytesRead = bytesRead; - break; - } - } return buffer; } @@ -737,7 +733,29 @@ namespace Renci.SshClient /// The data. protected void Write(byte[] data) { - this._socket.Send(data); + int sent = 0; // how many bytes is already sent + int length = data.Length; + + do + { + try + { + sent += this._socket.Send(data, sent, length - sent, SocketFlags.None); + } + catch (SocketException ex) + { + if (ex.SocketErrorCode == SocketError.WouldBlock || + ex.SocketErrorCode == SocketError.IOPending || + ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable) + { + // socket buffer is probably full, wait and try again + Thread.Sleep(30); + } + else + throw ex; // any serious error occurr + } + } while (sent < length); + } /// @@ -798,6 +816,36 @@ namespace Renci.SshClient #endregion + /// + /// Initializes this session. + /// + /// Initialization required when same session object being reconnected + private void Initialize() + { + // TODO: Consider reseting objects to initial stage instead of creating a new one + // Initialize session + this._outboundPacketSequence = 0; + this._inboundPacketSequence = 0; + this._openChannels = new Dictionary(); + this._keyExhangedFinishedWaitHandle = new AutoResetEvent(false); + this._serviceAccepted = new AutoResetEvent(false); + this._exceptionWaitHandle = new AutoResetEvent(false); + this._listenerWaitHandle = new AutoResetEvent(false); + this._channelNumbers = new BlockingStack(); + this._exceptionToThrow = null; + this.SessionId = null; + this.ServerVersion = null; + this._keyExhcange = null; + this.ServerMac = null; + this.ClientMac = null; + this.ClientCipher = null; + this.ServerCipher = null; + this.ServerDecompression = null; + this.ClientCompression = null; + this._isAuthenticated = false; + this._isDisconnecting = false; + } + private bool ValidateHash(List decryptedData, byte[] serverHash, uint packetSequence) { var clientHashData = new List(); @@ -831,6 +879,12 @@ namespace Renci.SshClient { this._socket.Close(); } + + if (this._messageListener != null) + { + this._messageListener.Wait(); + this._messageListener = null; + } } /// @@ -856,30 +910,27 @@ namespace Renci.SshClient this.RaiseMessageReceived(this, new MessageReceivedEventArgs(message)); } } - catch (IOException exp) - { - // Ignore this error since socket was disconected - - // Ensure socket is disconnected - this._socket.Close(); - - if (!this._isDisconnecting) - { - this._exceptionToThrow = exp; - - this._exceptionWaitHandle.Set(); - } - } catch (Exception exp) { // TODO: This exception can be swolloed if it occures while running in the background, look for possible solutions - // In case of error issue disconntect command - this.Disconnect(DisconnectReasonCodes.ByApplication, exp.ToString()); + // Ignore this error since socket was disconected + if (exp is SocketException && ((SocketException)exp).SocketErrorCode == SocketError.ConnectionAborted && this._isDisconnecting) + { + // Do nothing since connection was disconnected by the client + } + else + { + // In case of error issue disconntect command + this.Disconnect(DisconnectReasonCodes.ByApplication, exp.ToString()); - this._exceptionToThrow = exp; + this._exceptionToThrow = exp; - this._exceptionWaitHandle.Set(); + this._exceptionWaitHandle.Set(); + } + + // Ensure socket is disconnected + this._socket.Close(); } this._listenerWaitHandle.Set(); diff --git a/Renci.SshClient/Renci.SshClient/Shell.cs b/Renci.SshClient/Renci.SshClient/Shell.cs index 34ea15e7..c04ba0f0 100644 --- a/Renci.SshClient/Renci.SshClient/Shell.cs +++ b/Renci.SshClient/Renci.SshClient/Shell.cs @@ -76,6 +76,5 @@ namespace Renci.SshClient channelAsyncResult.Channel.EndExecute(asynchResult); } - } }