From 073767a9fe0d8df205bc9f68bcce0e21744f059f Mon Sep 17 00:00:00 2001 From: olegkap_cp Date: Tue, 31 Aug 2010 15:52:41 +0000 Subject: [PATCH] Minor channel communication improvemtns, disconecting and error handeling improved --- .../Renci.SshClient/Channels/Channel.cs | 15 ++++- .../Renci.SshClient/Channels/ChannelExec.cs | 52 +++++++++++------ .../Renci.SshClient/Common/BlockingStack.cs | 4 +- Renci.SshClient/Renci.SshClient/Session.cs | 58 +++++++++++++++---- 4 files changed, 93 insertions(+), 36 deletions(-) diff --git a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs index 9a514df9..1af100ff 100644 --- a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs +++ b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs @@ -13,7 +13,7 @@ namespace Renci.SshClient.Channels private uint _initialWindowSize = 0x100000; - private uint _maximumPacketSize = 0x4000; + private uint _maximumPacketSize = 0x8000; /// /// Counts faile channel open attempts @@ -196,10 +196,19 @@ namespace Renci.SshClient.Channels if (message.RequestName == RequestNames.ExitStatus) { var exitStatus = message.ExitStatus; + replyMessage = new ChannelSuccessMessage() { ChannelNumber = message.ChannelNumber, }; + + this.SendChannelCloseMessage(); + + // TODO: if exitStatus is not 0 then throw an exception or notify user that command failed to execute correctly + } + else + { + throw new NotImplementedException(string.Format("Request name {0} is not implemented.", message.RequestName)); } if (message.WantReply) @@ -229,6 +238,8 @@ namespace Renci.SshClient.Channels this._channelClosedWaitHandle.Set(); } + #endregion + private void AdjustDataWindow(string messageData) { this.WindowSize -= (uint)messageData.Length; @@ -245,8 +256,6 @@ namespace Renci.SshClient.Channels } } - #endregion - private void RaiseOpened() { if (this.Opened != null) diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs index a0864920..81974ef1 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, 0x100000, 0x1000) + : base(session, channelId, 0x100000, 0x8000) { } @@ -47,7 +47,6 @@ namespace Renci.SshClient.Channels }; this._callback = callback; - this._channelData = output; this._channelExtendedData = extendedOutput; @@ -67,26 +66,34 @@ namespace Renci.SshClient.Channels internal void EndExecute(IAsyncResult result) { - ChannelAsyncResult channelAsyncResult = result as ChannelAsyncResult; - - if (channelAsyncResult.Channel != this) + try { - throw new InvalidOperationException("Invalid IAsyncResult parameter"); + ChannelAsyncResult channelAsyncResult = result as ChannelAsyncResult; + + if (channelAsyncResult.Channel != this) + { + throw new InvalidOperationException("Invalid IAsyncResult parameter"); + } + + //Make sure that operation completed if not wait for it to finish + this.Session.WaitHandle(this._asyncResult.AsyncWaitHandle); + + this.Close(); + + this._asyncResult = null; + + if (this._exception != null) + { + var exception = this._exception; + this._exception = null; // Clean exception + throw exception; + } + } + catch (Exception exp) + { + throw; } - //Make sure that operation completed if not wait for it to finish - this.Session.WaitHandle(this._asyncResult.AsyncWaitHandle); - - this.Close(); - - this._asyncResult = null; - - if (this._exception != null) - { - var exception = this._exception; - this._exception = null; // Clean exception - throw exception; - } } protected override void OnChannelEof() @@ -96,6 +103,13 @@ namespace Renci.SshClient.Channels this.ExecutionCompleted(); } + protected override void OnChannelClose() + { + base.OnChannelClose(); + + this.ExecutionCompleted(); + } + protected override void OnChannelFailed(uint reasonCode, string description) { base.OnChannelFailed(reasonCode, description); diff --git a/Renci.SshClient/Renci.SshClient/Common/BlockingStack.cs b/Renci.SshClient/Renci.SshClient/Common/BlockingStack.cs index b9b195fc..de4c50b7 100644 --- a/Renci.SshClient/Renci.SshClient/Common/BlockingStack.cs +++ b/Renci.SshClient/Renci.SshClient/Common/BlockingStack.cs @@ -126,12 +126,12 @@ namespace Renci.SshClient.Common public bool IsSynchronized { - get { throw new System.NotImplementedException(); } + get { return true; } } public object SyncRoot { - get { throw new System.NotImplementedException(); } + get { return this; } } #endregion diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs index 4ebafd2b..7b249d6b 100644 --- a/Renci.SshClient/Renci.SshClient/Session.cs +++ b/Renci.SshClient/Renci.SshClient/Session.cs @@ -72,6 +72,11 @@ namespace Renci.SshClient /// private EventWaitHandle _exceptionWaitHandle = new AutoResetEvent(false); + /// + /// WaitHandle to signal that listner ended + /// + private EventWaitHandle _listenerWaitHandle = new AutoResetEvent(false); + /// /// Keeps track of all open channels /// @@ -87,6 +92,11 @@ namespace Renci.SshClient /// private bool _isAuthenticated; + /// + /// Specifies weither Disconnect method was called + /// + private bool _isDisconnecting; + /// /// holds number to be used for session channels /// @@ -233,15 +243,18 @@ namespace Renci.SshClient if (this.IsConnected) return; - lock (this) + try { - // If connected dont connect again + _authenticationConnection.Wait(); + if (this.IsConnected) return; - try + lock (this) { - _authenticationConnection.Wait(); + // If connected dont connect again + if (this.IsConnected) + return; var ep = new IPEndPoint(Dns.GetHostAddresses(connectionInfo.Host)[0], connectionInfo.Port); this._socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); @@ -331,11 +344,13 @@ namespace Renci.SshClient { throw new AuthenticationException(errorMessage ?? "User cannot be authenticated."); } + + Monitor.Pulse(this); } - finally - { - _authenticationConnection.Release(); - } + } + finally + { + _authenticationConnection.Release(); } } @@ -344,7 +359,8 @@ namespace Renci.SshClient /// public void Disconnect() { - // TODO: Change message to something more appropriate + this._isDisconnecting = true; + this.Disconnect(DisconnectReasonCodes.ByApplication, "Connection terminated by the client."); this.DisconnectCleanup(); @@ -359,6 +375,7 @@ namespace Renci.SshClient var waitHandles = new WaitHandle[] { this._exceptionWaitHandle, + this._listenerWaitHandle, // When listener exits waitHandle, }; @@ -491,7 +508,7 @@ namespace Renci.SshClient // Test packet minimum and maximum boundaries if (packetLength < Math.Max((byte)16, blockSize) - 4 || packetLength > Session.MAXIMUM_PACKET_SIZE - 4) - throw new InvalidOperationException(string.Format("Bad packet length {0}", packetLength)); + throw new IOException(string.Format("Bad packet length {0}", packetLength)); // Read rest of the packet data int bytesToRead = (int)(packetLength - (blockSize - 4)); @@ -525,7 +542,7 @@ namespace Renci.SshClient if (!serverHash.IsEqualTo(clientHash)) { - throw new InvalidOperationException("MAC error"); + throw new IOException("MAC error"); } } @@ -839,9 +856,19 @@ namespace Renci.SshClient this.RaiseMessageReceived(this, new MessageReceivedEventArgs(message)); } } - catch (IOException) + 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) { @@ -854,6 +881,8 @@ namespace Renci.SshClient this._exceptionWaitHandle.Set(); } + + this._listenerWaitHandle.Set(); } /// @@ -910,6 +939,11 @@ namespace Renci.SshClient { this._exceptionWaitHandle.Dispose(); } + + if (this._listenerWaitHandle != null) + { + this._listenerWaitHandle.Dispose(); + } } // Note disposing has been done.