| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- using System.Diagnostics;
- using System.Threading;
- using Renci.SshNet.Common;
- using Renci.SshNet.Messages.Connection;
- using System.Globalization;
- namespace Renci.SshNet.Channels
- {
- /// <summary>
- /// Implements Session SSH channel.
- /// </summary>
- internal class ChannelSession : Channel
- {
- /// <summary>
- /// Counts faile channel open attempts
- /// </summary>
- private int _failedOpenAttempts;
- /// <summary>
- /// Wait handle to signal when response was received to open the channel
- /// </summary>
- private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false);
- private EventWaitHandle _channelRequestResponse = new ManualResetEvent(false);
- private bool _channelRequestSucces;
- /// <summary>
- /// Gets the type of the channel.
- /// </summary>
- /// <value>
- /// The type of the channel.
- /// </value>
- public override ChannelTypes ChannelType
- {
- get { return ChannelTypes.Session; }
- }
- /// <summary>
- /// Opens the channel.
- /// </summary>
- public virtual void Open()
- {
- if (!this.IsOpen)
- {
- // Try to open channel several times
- while (this._failedOpenAttempts < this.ConnectionInfo.RetryAttempts && !this.IsOpen)
- {
- this.SendChannelOpenMessage();
- this.WaitHandle(this._channelOpenResponseWaitHandle);
- }
- if (!this.IsOpen)
- {
- throw new SshException(string.Format(CultureInfo.CurrentCulture, "Failed to open a channel after {0} attempts.", this._failedOpenAttempts));
- }
- }
- }
- /// <summary>
- /// Called when channel is opened by the server.
- /// </summary>
- /// <param name="remoteChannelNumber">The remote channel number.</param>
- /// <param name="initialWindowSize">Initial size of the window.</param>
- /// <param name="maximumPacketSize">Maximum size of the packet.</param>
- protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize)
- {
- base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize);
- this._channelOpenResponseWaitHandle.Set();
- }
- /// <summary>
- /// Called when channel failed to open.
- /// </summary>
- /// <param name="reasonCode">The reason code.</param>
- /// <param name="description">The description.</param>
- /// <param name="language">The language.</param>
- protected override void OnOpenFailure(uint reasonCode, string description, string language)
- {
- this._failedOpenAttempts++;
- Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Local channel: {0} attempts: {1}.", this.LocalChannelNumber, this._failedOpenAttempts));
- this.SessionSemaphore.Release();
- this._channelOpenResponseWaitHandle.Set();
- }
- /// <summary>
- /// Called when channel is closed by the server.
- /// </summary>
- 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();
- }
- /// <summary>
- /// Sends the pseudo terminal request.
- /// </summary>
- /// <param name="environmentVariable">The environment variable.</param>
- /// <param name="columns">The columns.</param>
- /// <param name="rows">The rows.</param>
- /// <param name="width">The width.</param>
- /// <param name="height">The height.</param>
- /// <param name="terminalMode">The terminal mode.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, string terminalMode)
- {
- this._channelRequestResponse.Reset();
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalMode)));
- this._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Sends the X11 forwarding request.
- /// </summary>
- /// <param name="isSingleConnection">if set to <c>true</c> the it is single connection.</param>
- /// <param name="protocol">The protocol.</param>
- /// <param name="cookie">The cookie.</param>
- /// <param name="screenNumber">The screen number.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- 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._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Sends the environment variable request.
- /// </summary>
- /// <param name="variableName">Name of the variable.</param>
- /// <param name="variableValue">The variable value.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendEnvironmentVariableRequest(string variableName, string variableValue)
- {
- this._channelRequestResponse.Reset();
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, variableValue)));
- this._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Sends the shell request.
- /// </summary>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendShellRequest()
- {
- this._channelRequestResponse.Reset();
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ShellRequestInfo()));
- this._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Sends the exec request.
- /// </summary>
- /// <param name="command">The command.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendExecRequest(string command)
- {
- this._channelRequestResponse.Reset();
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExecRequestInfo(command)));
- this._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Sends the exec request.
- /// </summary>
- /// <param name="breakLength">Length of the break.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendBreakRequest(uint breakLength)
- {
- this._channelRequestResponse.Reset();
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new BreakRequestInfo(breakLength)));
- this._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Sends the subsystem request.
- /// </summary>
- /// <param name="subsystem">The subsystem.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendSubsystemRequest(string subsystem)
- {
- this._channelRequestResponse.Reset();
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SubsystemRequestInfo(subsystem)));
- this._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Sends the window change request.
- /// </summary>
- /// <param name="columns">The columns.</param>
- /// <param name="rows">The rows.</param>
- /// <param name="width">The width.</param>
- /// <param name="height">The height.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- 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;
- }
- /// <summary>
- /// Sends the local flow request.
- /// </summary>
- /// <param name="clientCanDo">if set to <c>true</c> [client can do].</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendLocalFlowRequest(bool clientCanDo)
- {
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new XonXoffRequestInfo(clientCanDo)));
- return true;
- }
- /// <summary>
- /// Sends the signal request.
- /// </summary>
- /// <param name="signalName">Name of the signal.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendSignalRequest(string signalName)
- {
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SignalRequestInfo(signalName)));
- return true;
- }
- /// <summary>
- /// Sends the exit status request.
- /// </summary>
- /// <param name="exitStatus">The exit status.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendExitStatusRequest(uint exitStatus)
- {
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExitStatusRequestInfo(exitStatus)));
- return true;
- }
- /// <summary>
- /// Sends the exit signal request.
- /// </summary>
- /// <param name="signalName">Name of the signal.</param>
- /// <param name="coreDumped">if set to <c>true</c> [core dumped].</param>
- /// <param name="errorMessage">The error message.</param>
- /// <param name="language">The language.</param>
- /// <returns>true if request was successful; otherwise false.</returns>
- 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;
- }
- /// <summary>
- /// Sends eow@openssh.com request.
- /// </summary>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendEndOfWriteRequest()
- {
- this._channelRequestResponse.Reset();
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EndOfWriteRequestInfo()));
- this._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Sends keepalive@openssh.com request.
- /// </summary>
- /// <returns>true if request was successful; otherwise false.</returns>
- public bool SendKeepAliveRequest()
- {
- this._channelRequestResponse.Reset();
- this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new KeepAliveRequestInfo()));
- this._channelRequestResponse.WaitOne();
- return this._channelRequestSucces;
- }
- /// <summary>
- /// Called when channel request was successful
- /// </summary>
- protected override void OnSuccess()
- {
- base.OnSuccess();
- this._channelRequestSucces = true;
- this._channelRequestResponse.Set();
- }
- /// <summary>
- /// Called when channel request failed.
- /// </summary>
- protected override void OnFailure()
- {
- base.OnFailure();
- this._channelRequestSucces = false;
- this._channelRequestResponse.Set();
- }
- /// <summary>
- /// Sends the channel open message.
- /// </summary>
- protected void SendChannelOpenMessage()
- {
- lock (this.SessionSemaphore)
- {
- // Ensure that channels are available
- this.SessionSemaphore.Wait();
- this.SendMessage(new ChannelOpenMessage(this.LocalChannelNumber, this.LocalWindowSize, this.PacketSize, new SessionChannelOpenInfo()));
- }
- }
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- 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);
- }
- }
- }
|