using System.Collections.Generic; using System.Globalization; using System.Text; using System.Threading; using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; namespace Renci.SshNet.Channels { /// /// Implements Session SSH channel. /// internal class ChannelSession : ClientChannel, IChannelSession { /// /// Counts faile channel open attempts /// private int _failedOpenAttempts; /// /// Wait handle to signal when response was received to open the channel /// private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false); private EventWaitHandle _channelRequestResponse = new ManualResetEvent(false); private bool _channelRequestSucces; /// /// Gets the type of the channel. /// /// /// The type of the channel. /// public override ChannelTypes ChannelType { get { return ChannelTypes.Session; } } /// /// Opens the channel. /// public virtual void Open() { if (!this.IsOpen) { // Try to open channel several times while (this._failedOpenAttempts < this.ConnectionInfo.RetryAttempts && !this.IsOpen) { this.SendChannelOpenMessage(); this.WaitOnHandle(this._channelOpenResponseWaitHandle); } if (!this.IsOpen) throw new SshException(string.Format(CultureInfo.CurrentCulture, "Failed to open a channel after {0} attempts.", this._failedOpenAttempts)); } } /// /// Called when channel is opened by the server. /// /// The remote channel number. /// Initial size of the window. /// Maximum size of the packet. protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize) { base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize); this._channelOpenResponseWaitHandle.Set(); } /// /// Called when channel failed to open. /// /// The reason code. /// The description. /// The language. protected override void OnOpenFailure(uint reasonCode, string description, string language) { this._failedOpenAttempts++; this.SessionSemaphore.Release(); this._channelOpenResponseWaitHandle.Set(); } /// /// Called when channel is closed by the server. /// protected override void OnClose() { base.OnClose(); // This timeout needed since when channel is closed it does not immediately becomes available // but it takes time for the server to clean up resource and allow new channels to be created. Thread.Sleep(100); this.SessionSemaphore.Release(); } protected override void Close(bool wait) { base.Close(wait); if (!wait) this.SessionSemaphore.Release(); } /// /// Sends the pseudo terminal request. /// /// The environment variable. /// The columns. /// The rows. /// The width. /// The height. /// The terminal mode values. /// /// true if request was successful; otherwise false. /// public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues) { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalModeValues))); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Sends the X11 forwarding request. /// /// if set to true the it is single connection. /// The protocol. /// The cookie. /// The screen number. /// /// true if request was successful; otherwise false. /// public bool SendX11ForwardingRequest(bool isSingleConnection, string protocol, byte[] cookie, uint screenNumber) { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new X11ForwardingRequestInfo(isSingleConnection, protocol, cookie, screenNumber))); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Sends the environment variable request. /// /// Name of the variable. /// The variable value. /// /// true if request was successful; otherwise false. /// public bool SendEnvironmentVariableRequest(string variableName, string variableValue) { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, variableValue))); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Sends the shell request. /// /// /// true if request was successful; otherwise false. /// public bool SendShellRequest() { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ShellRequestInfo())); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Sends the exec request. /// /// The command. /// /// true if request was successful; otherwise false. /// public bool SendExecRequest(string command) { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExecRequestInfo(command, this.ConnectionInfo.Encoding))); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Sends the exec request. /// /// Length of the break. /// /// true if request was successful; otherwise false. /// public bool SendBreakRequest(uint breakLength) { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new BreakRequestInfo(breakLength))); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Sends the subsystem request. /// /// The subsystem. /// /// true if request was successful; otherwise false. /// public bool SendSubsystemRequest(string subsystem) { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SubsystemRequestInfo(subsystem))); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Sends the window change request. /// /// The columns. /// The rows. /// The width. /// The height. /// /// true if request was successful; otherwise false. /// public bool SendWindowChangeRequest(uint columns, uint rows, uint width, uint height) { this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new WindowChangeRequestInfo(columns, rows, width, height))); return true; } /// /// Sends the local flow request. /// /// if set to true [client can do]. /// /// true if request was successful; otherwise false. /// public bool SendLocalFlowRequest(bool clientCanDo) { this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new XonXoffRequestInfo(clientCanDo))); return true; } /// /// Sends the signal request. /// /// Name of the signal. /// /// true if request was successful; otherwise false. /// public bool SendSignalRequest(string signalName) { this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SignalRequestInfo(signalName))); return true; } /// /// Sends the exit status request. /// /// The exit status. /// /// true if request was successful; otherwise false. /// public bool SendExitStatusRequest(uint exitStatus) { this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitStatusRequestInfo(exitStatus))); return true; } /// /// Sends the exit signal request. /// /// Name of the signal. /// if set to true [core dumped]. /// The error message. /// The language. /// /// true if request was successful; otherwise false. /// public bool SendExitSignalRequest(string signalName, bool coreDumped, string errorMessage, string language) { this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitSignalRequestInfo(signalName, coreDumped, errorMessage, language))); return true; } /// /// Sends eow@openssh.com request. /// /// /// true if request was successful; otherwise false. /// public bool SendEndOfWriteRequest() { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EndOfWriteRequestInfo())); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Sends keepalive@openssh.com request. /// /// /// true if request was successful; otherwise false. /// public bool SendKeepAliveRequest() { this._channelRequestResponse.Reset(); this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new KeepAliveRequestInfo())); this.WaitOnHandle(this._channelRequestResponse); return this._channelRequestSucces; } /// /// Called when channel request was successful /// protected override void OnSuccess() { base.OnSuccess(); this._channelRequestSucces = true; var channelRequestResponse = _channelRequestResponse; if (channelRequestResponse != null) channelRequestResponse.Set(); } /// /// Called when channel request failed. /// protected override void OnFailure() { base.OnFailure(); _channelRequestSucces = false; var channelRequestResponse = _channelRequestResponse; if (channelRequestResponse != null) channelRequestResponse.Set(); } /// /// Sends the channel open message. /// protected void SendChannelOpenMessage() { lock (this.SessionSemaphore) { // Ensure that channels are available this.SessionSemaphore.Wait(); this.SendMessage(new ChannelOpenMessage(this.LocalChannelNumber, this.LocalWindowSize, this.LocalPacketSize, new SessionChannelOpenInfo())); } } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (this._channelOpenResponseWaitHandle != null) { this._channelOpenResponseWaitHandle.Dispose(); this._channelOpenResponseWaitHandle = null; } if (this._channelRequestResponse != null) { this._channelRequestResponse.Dispose(); this._channelRequestResponse = null; } base.Dispose(disposing); } } }