< Summary

Information
Class: Renci.SshNet.Channels.ChannelSession
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\Channels\ChannelSession.cs
Line coverage
66%
Covered lines: 102
Uncovered lines: 51
Coverable lines: 153
Total lines: 450
Line coverage: 66.6%
Branch coverage
90%
Covered branches: 18
Total branches: 20
Branch coverage: 90%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\Channels\ChannelSession.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4using System.Threading;
 5
 6using Renci.SshNet.Common;
 7using Renci.SshNet.Messages.Connection;
 8
 9namespace Renci.SshNet.Channels
 10{
 11    /// <summary>
 12    /// Implements Session SSH channel.
 13    /// </summary>
 14    internal sealed class ChannelSession : ClientChannel, IChannelSession
 15    {
 16        /// <summary>
 17        /// Counts failed channel open attempts.
 18        /// </summary>
 19        private int _failedOpenAttempts;
 20
 21        /// <summary>
 22        /// Holds a value indicating whether the session semaphore has been obtained by the current
 23        /// channel.
 24        /// </summary>
 25        /// <value>
 26        /// <c>0</c> when the session semaphore has not been obtained or has already been released,
 27        /// and <c>1</c> when the session has been obtained and still needs to be released.
 28        /// </value>
 29        private int _sessionSemaphoreObtained;
 30
 31        /// <summary>
 32        /// Wait handle to signal when response was received to open the channel.
 33        /// </summary>
 193434        private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(initialState: false);
 35
 193436        private EventWaitHandle _channelRequestResponse = new ManualResetEvent(initialState: false);
 37
 38        private bool _channelRequestSucces;
 39
 40        /// <summary>
 41        /// Initializes a new instance of the <see cref="ChannelSession"/> class.
 42        /// </summary>
 43        /// <param name="session">The session.</param>
 44        /// <param name="localChannelNumber">The local channel number.</param>
 45        /// <param name="localWindowSize">Size of the window.</param>
 46        /// <param name="localPacketSize">Size of the packet.</param>
 47        public ChannelSession(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize)
 193448            : base(session, localChannelNumber, localWindowSize, localPacketSize)
 193449        {
 193450        }
 51
 52        /// <summary>
 53        /// Gets the type of the channel.
 54        /// </summary>
 55        /// <value>
 56        /// The type of the channel.
 57        /// </value>
 58        public override ChannelTypes ChannelType
 59        {
 060            get { return ChannelTypes.Session; }
 61        }
 62
 63        /// <summary>
 64        /// Opens the channel.
 65        /// </summary>
 66        public void Open()
 193467        {
 68            // Try to open channel several times
 392269            while (!IsOpen && _failedOpenAttempts < ConnectionInfo.RetryAttempts)
 200370            {
 200371                SendChannelOpenMessage();
 72                try
 200373                {
 200374                    WaitOnHandle(_channelOpenResponseWaitHandle);
 198875                }
 1576                catch (Exception)
 1577                {
 78                    // avoid leaking session semaphore
 1579                    ReleaseSemaphore();
 1580                    throw;
 81                }
 198882            }
 83
 191984            if (!IsOpen)
 1585            {
 1586                throw new SshException(string.Format(CultureInfo.CurrentCulture, "Failed to open a channel after {0} att
 87            }
 190488        }
 89
 90        /// <summary>
 91        /// Called when channel is opened by the server.
 92        /// </summary>
 93        /// <param name="remoteChannelNumber">The remote channel number.</param>
 94        /// <param name="initialWindowSize">Initial size of the window.</param>
 95        /// <param name="maximumPacketSize">Maximum size of the packet.</param>
 96        protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketS
 190497        {
 190498            base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize);
 99
 1904100            _ = _channelOpenResponseWaitHandle.Set();
 1904101        }
 102
 103        /// <summary>
 104        /// Called when channel failed to open.
 105        /// </summary>
 106        /// <param name="reasonCode">The reason code.</param>
 107        /// <param name="description">The description.</param>
 108        /// <param name="language">The language.</param>
 109        protected override void OnOpenFailure(uint reasonCode, string description, string language)
 84110        {
 84111            _failedOpenAttempts++;
 84112            ReleaseSemaphore();
 84113            _ = _channelOpenResponseWaitHandle.Set();
 84114        }
 115
 116        protected override void Close()
 3741117        {
 3741118            base.Close();
 3741119            ReleaseSemaphore();
 3741120        }
 121
 122        /// <summary>
 123        /// Sends the pseudo terminal request.
 124        /// </summary>
 125        /// <param name="environmentVariable">The environment variable.</param>
 126        /// <param name="columns">The columns.</param>
 127        /// <param name="rows">The rows.</param>
 128        /// <param name="width">The width.</param>
 129        /// <param name="height">The height.</param>
 130        /// <param name="terminalModeValues">The terminal mode values.</param>
 131        /// <returns>
 132        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 133        /// </returns>
 134        public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint heig
 7135        {
 7136            _ = _channelRequestResponse.Reset();
 7137            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable
 7138            WaitOnHandle(_channelRequestResponse);
 7139            return _channelRequestSucces;
 7140        }
 141
 142        /// <summary>
 143        /// Sends the X11 forwarding request.
 144        /// </summary>
 145        /// <param name="isSingleConnection">if set to <see langword="true"/> the it is single connection.</param>
 146        /// <param name="protocol">The protocol.</param>
 147        /// <param name="cookie">The cookie.</param>
 148        /// <param name="screenNumber">The screen number.</param>
 149        /// <returns>
 150        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 151        /// </returns>
 152        public bool SendX11ForwardingRequest(bool isSingleConnection, string protocol, byte[] cookie, uint screenNumber)
 0153        {
 0154            _ = _channelRequestResponse.Reset();
 0155            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new X11ForwardingRequestInfo(isSingleConnection, 
 0156            WaitOnHandle(_channelRequestResponse);
 0157            return _channelRequestSucces;
 0158        }
 159
 160        /// <summary>
 161        /// Sends the environment variable request.
 162        /// </summary>
 163        /// <param name="variableName">Name of the variable.</param>
 164        /// <param name="variableValue">The variable value.</param>
 165        /// <returns>
 166        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 167        /// </returns>
 168        public bool SendEnvironmentVariableRequest(string variableName, string variableValue)
 0169        {
 0170            _ = _channelRequestResponse.Reset();
 0171            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, 
 0172            WaitOnHandle(_channelRequestResponse);
 0173            return _channelRequestSucces;
 0174        }
 175
 176        /// <summary>
 177        /// Sends the shell request.
 178        /// </summary>
 179        /// <returns>
 180        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 181        /// </returns>
 182        public bool SendShellRequest()
 7183        {
 7184            _ = _channelRequestResponse.Reset();
 7185            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ShellRequestInfo()));
 7186            WaitOnHandle(_channelRequestResponse);
 7187            return _channelRequestSucces;
 7188        }
 189
 190        /// <summary>
 191        /// Sends the exec request.
 192        /// </summary>
 193        /// <param name="command">The command.</param>
 194        /// <returns>
 195        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 196        /// </returns>
 197        public bool SendExecRequest(string command)
 1075198        {
 1075199            _ = _channelRequestResponse.Reset();
 1075200            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ExecRequestInfo(command, ConnectionInfo.Encod
 1075201            WaitOnHandle(_channelRequestResponse);
 1075202            return _channelRequestSucces;
 1075203        }
 204
 205        /// <summary>
 206        /// Sends the exec request.
 207        /// </summary>
 208        /// <param name="breakLength">Length of the break.</param>
 209        /// <returns>
 210        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 211        /// </returns>
 212        public bool SendBreakRequest(uint breakLength)
 0213        {
 0214            _ = _channelRequestResponse.Reset();
 0215            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new BreakRequestInfo(breakLength)));
 0216            WaitOnHandle(_channelRequestResponse);
 0217            return _channelRequestSucces;
 0218        }
 219
 220        /// <summary>
 221        /// Sends the subsystem request.
 222        /// </summary>
 223        /// <param name="subsystem">The subsystem.</param>
 224        /// <returns>
 225        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 226        /// </returns>
 227        public bool SendSubsystemRequest(string subsystem)
 618228        {
 618229            _ = _channelRequestResponse.Reset();
 618230            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new SubsystemRequestInfo(subsystem)));
 618231            WaitOnHandle(_channelRequestResponse);
 618232            return _channelRequestSucces;
 618233        }
 234
 235        /// <summary>
 236        /// Sends the window change request.
 237        /// </summary>
 238        /// <param name="columns">The columns.</param>
 239        /// <param name="rows">The rows.</param>
 240        /// <param name="width">The width.</param>
 241        /// <param name="height">The height.</param>
 242        /// <returns>
 243        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 244        /// </returns>
 245        public bool SendWindowChangeRequest(uint columns, uint rows, uint width, uint height)
 0246        {
 0247            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new WindowChangeRequestInfo(columns, rows, width,
 0248            return true;
 0249        }
 250
 251        /// <summary>
 252        /// Sends the local flow request.
 253        /// </summary>
 254        /// <param name="clientCanDo">if set to <see langword="true"/> [client can do].</param>
 255        /// <returns>
 256        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 257        /// </returns>
 258        public bool SendLocalFlowRequest(bool clientCanDo)
 0259        {
 0260            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new XonXoffRequestInfo(clientCanDo)));
 0261            return true;
 0262        }
 263
 264        /// <summary>
 265        /// Sends the signal request.
 266        /// </summary>
 267        /// <param name="signalName">Name of the signal.</param>
 268        /// <returns>
 269        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 270        /// </returns>
 271        public bool SendSignalRequest(string signalName)
 0272        {
 0273            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new SignalRequestInfo(signalName)));
 0274            return true;
 0275        }
 276
 277        /// <summary>
 278        /// Sends the exit status request.
 279        /// </summary>
 280        /// <param name="exitStatus">The exit status.</param>
 281        /// <returns>
 282        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 283        /// </returns>
 284        public bool SendExitStatusRequest(uint exitStatus)
 0285        {
 0286            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ExitStatusRequestInfo(exitStatus)));
 0287            return true;
 0288        }
 289
 290        /// <summary>
 291        /// Sends the exit signal request.
 292        /// </summary>
 293        /// <param name="signalName">Name of the signal.</param>
 294        /// <param name="coreDumped">if set to <see langword="true"/> [core dumped].</param>
 295        /// <param name="errorMessage">The error message.</param>
 296        /// <param name="language">The language.</param>
 297        /// <returns>
 298        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 299        /// </returns>
 300        public bool SendExitSignalRequest(string signalName, bool coreDumped, string errorMessage, string language)
 0301        {
 0302            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new ExitSignalRequestInfo(signalName, coreDumped,
 0303            return true;
 0304        }
 305
 306        /// <summary>
 307        /// Sends eow@openssh.com request.
 308        /// </summary>
 309        /// <returns>
 310        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 311        /// </returns>
 312        public bool SendEndOfWriteRequest()
 0313        {
 0314            _ = _channelRequestResponse.Reset();
 0315            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new EndOfWriteRequestInfo()));
 0316            WaitOnHandle(_channelRequestResponse);
 0317            return _channelRequestSucces;
 0318        }
 319
 320        /// <summary>
 321        /// Sends keepalive@openssh.com request.
 322        /// </summary>
 323        /// <returns>
 324        /// <see langword="true"/> if request was successful; otherwise <see langword="false"/>.
 325        /// </returns>
 326        public bool SendKeepAliveRequest()
 0327        {
 0328            _ = _channelRequestResponse.Reset();
 0329            SendMessage(new ChannelRequestMessage(RemoteChannelNumber, new KeepAliveRequestInfo()));
 0330            WaitOnHandle(_channelRequestResponse);
 0331            return _channelRequestSucces;
 0332        }
 333
 334        /// <summary>
 335        /// Called when channel request was successful.
 336        /// </summary>
 337        protected override void OnSuccess()
 1706338        {
 1706339            base.OnSuccess();
 340
 1706341            _channelRequestSucces = true;
 1706342            _ = _channelRequestResponse?.Set();
 1706343        }
 344
 345        /// <summary>
 346        /// Called when channel request failed.
 347        /// </summary>
 348        protected override void OnFailure()
 1349        {
 1350            base.OnFailure();
 351
 1352            _channelRequestSucces = false;
 1353            _ = _channelRequestResponse?.Set();
 1354        }
 355
 356        /// <summary>
 357        /// Sends the channel open message.
 358        /// </summary>
 359        /// <exception cref="SshConnectionException">The client is not connected.</exception>
 360        /// <exception cref="SshOperationTimeoutException">The operation timed out.</exception>
 361        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the p
 362        /// <remarks>
 363        /// <para>
 364        /// When a session semaphore for this instance has not yet been obtained by this or any other thread,
 365        /// the thread will block until such a semaphore is available and send a <see cref="ChannelOpenMessage"/>
 366        /// to the remote host.
 367        /// </para>
 368        /// <para>
 369        /// Note that the session semaphore is released in any of the following cases:
 370        /// <list type="bullet">
 371        ///   <item>
 372        ///     <description>A <see cref="ChannelOpenFailureMessage"/> is received for the channel being opened.</descri
 373        ///   </item>
 374        ///   <item>
 375        ///     <description>The remote host does not respond to the <see cref="ChannelOpenMessage"/> within the configu
 376        ///   </item>
 377        ///   <item>
 378        ///     <description>The remote host closes the channel.</description>
 379        ///   </item>
 380        ///   <item>
 381        ///     <description>The <see cref="ChannelSession"/> is disposed.</description>
 382        ///   </item>
 383        ///   <item>
 384        ///     <description>A socket error occurs sending a message to the remote host.</description>
 385        ///   </item>
 386        /// </list>
 387        /// </para>
 388        /// <para>
 389        /// If the session semaphore was already obtained for this instance (and not released), then this method
 390        /// immediately returns control to the caller. This should only happen when another thread has obtain the
 391        /// session semaphore and already sent the <see cref="ChannelOpenMessage"/>, but the remote host did not
 392        /// confirmed or rejected attempt to open the channel.
 393        /// </para>
 394        /// </remarks>
 395        private void SendChannelOpenMessage()
 2003396        {
 397            // do not allow the ChannelOpenMessage to be sent again until we've
 398            // had a response on the previous attempt for the current channel
 2003399            if (Interlocked.CompareExchange(ref _sessionSemaphoreObtained, 1, 0) == 0)
 2003400            {
 2003401                SessionSemaphore.Wait();
 2003402                SendMessage(new ChannelOpenMessage(LocalChannelNumber,
 2003403                                                   LocalWindowSize,
 2003404                                                   LocalPacketSize,
 2003405                                                   new SessionChannelOpenInfo()));
 2003406            }
 2003407        }
 408
 409        /// <summary>
 410        /// Releases unmanaged and - optionally - managed resources.
 411        /// </summary>
 412        /// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langwor
 413        protected override void Dispose(bool disposing)
 1977414        {
 1977415            base.Dispose(disposing);
 416
 1977417            if (disposing)
 1922418            {
 1922419                var channelOpenResponseWaitHandle = _channelOpenResponseWaitHandle;
 1922420                if (channelOpenResponseWaitHandle != null)
 1879421                {
 1879422                    _channelOpenResponseWaitHandle = null;
 1879423                    channelOpenResponseWaitHandle.Dispose();
 1879424                }
 425
 1922426                var channelRequestResponse = _channelRequestResponse;
 1922427                if (channelRequestResponse != null)
 1879428                {
 1879429                    _channelRequestResponse = null;
 1879430                    channelRequestResponse.Dispose();
 1879431                }
 1922432            }
 1977433        }
 434
 435        /// <summary>
 436        /// Releases the session semaphore.
 437        /// </summary>
 438        /// <remarks>
 439        /// When the session semaphore has already been released, or was never obtained by
 440        /// this instance, then this method does nothing.
 441        /// </remarks>
 442        private void ReleaseSemaphore()
 3840443        {
 3840444            if (Interlocked.CompareExchange(ref _sessionSemaphoreObtained, 0, 1) == 1)
 1990445            {
 1990446                _ = SessionSemaphore.Release();
 1990447            }
 3840448        }
 449    }
 450}