< Summary

Information
Class: Renci.SshNet.Session
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\Session.cs
Line coverage
88%
Covered lines: 701
Uncovered lines: 87
Coverable lines: 788
Total lines: 2207
Line coverage: 88.9%
Branch coverage
70%
Covered branches: 185
Total branches: 264
Branch coverage: 70%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.cctor()100%1100%
.ctor(...)100%6100%
get_SessionSemaphore()100%4100%
get_NextChannelNumber()100%1100%
get_IsConnected()90%10100%
get_SessionId()100%1100%
get_ClientInitMessage()100%2100%
get_ServerVersion()100%1100%
get_ClientVersion()100%1100%
get_ConnectionInfo()100%1100%
Connect()56.25%1679.71%
ConnectAsync()50%1281.48%
Disconnect()100%2100%
Disconnect(...)100%2100%
Renci.SshNet.ISession.WaitOnHandle(...)100%1100%
Renci.SshNet.ISession.WaitOnHandle(...)100%166.66%
Renci.SshNet.ISession.TryWait(...)100%1100%
Renci.SshNet.ISession.TryWait(...)100%1100%
TryWait(...)90%1096%
WaitOnHandle(...)100%1100%
WaitOnHandle(...)60%1072.72%
SendMessage(...)93.75%1695.23%
SendPacket(...)100%2100%
TrySendMessage(...)100%169.23%
ReceiveMessage(...)76.92%2675.86%
TrySendDisconnect(...)100%1100%
OnDisconnectReceived(...)50%4100%
OnIgnoreReceived(...)50%2100%
OnUnimplementedReceived(...)0%20%
OnDebugReceived(...)50%2100%
OnServiceRequestReceived(...)0%20%
OnServiceAcceptReceived(...)50%2100%
OnKeyExchangeDhGroupExchangeGroupReceived(...)50%2100%
OnKeyExchangeDhGroupExchangeReplyReceived(...)50%2100%
OnKeyExchangeInitReceived(...)50%2100%
OnKeyExchangeDhReplyMessageReceived(...)50%2100%
OnKeyExchangeEcdhReplyMessageReceived(...)50%2100%
OnNewKeysReceived(...)58.33%1258.82%
Renci.SshNet.ISession.OnDisconnecting()100%1100%
OnUserAuthenticationRequestReceived(...)0%20%
OnUserAuthenticationFailureReceived(...)50%2100%
OnUserAuthenticationSuccessReceived(...)50%2100%
OnUserAuthenticationBannerReceived(...)0%20%
OnUserAuthenticationInformationRequestReceived(...)50%2100%
OnUserAuthenticationPasswordChangeRequiredReceived(...)0%20%
OnUserAuthenticationPublicKeyReceived(...)50%2100%
OnGlobalRequestReceived(...)50%2100%
OnRequestSuccessReceived(...)100%2100%
OnRequestFailureReceived(...)0%20%
OnChannelOpenReceived(...)50%2100%
OnChannelOpenConfirmationReceived(...)50%2100%
OnChannelOpenFailureReceived(...)50%2100%
OnChannelWindowAdjustReceived(...)50%2100%
OnChannelDataReceived(...)50%2100%
OnChannelExtendedDataReceived(...)50%2100%
OnChannelEofReceived(...)50%2100%
OnChannelCloseReceived(...)50%2100%
OnChannelRequestReceived(...)50%2100%
OnChannelSuccessReceived(...)50%2100%
OnChannelFailureReceived(...)50%2100%
KeyExchange_HostKeyReceived(...)50%2100%
RegisterMessage(...)100%1100%
UnRegisterMessage(...)100%1100%
LoadMessage(...)100%1100%
ToHex(...)100%2100%
ToHex(...)100%2100%
IsSocketConnected()100%4100%
TrySocketRead(...)100%1100%
SocketDisconnectAndDispose()75%884.61%
MessageListener()90%1091.89%
RaiseError(...)58.33%1280.95%
Reset()50%6100%
CreateConnectionAbortedByServerException()100%1100%
Dispose()100%1100%
Dispose(...)95.45%2296.77%
Finalize()100%1100%
Renci.SshNet.ISession.get_ConnectionInfo()100%1100%
Renci.SshNet.ISession.get_MessageListenerCompleted()100%1100%
Renci.SshNet.ISession.CreateChannelSession()100%1100%
Renci.SshNet.ISession.CreateChannelDirectTcpip()100%1100%
Renci.SshNet.ISession.CreateChannelForwardedTcpip(...)100%1100%
Renci.SshNet.ISession.SendMessage(...)100%1100%
Renci.SshNet.ISession.TrySendMessage(...)100%1100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Globalization;
 3using System.Linq;
 4using System.Net.Sockets;
 5using System.Security.Cryptography;
 6using System.Text;
 7using System.Threading;
 8using System.Threading.Tasks;
 9
 10using Renci.SshNet.Abstractions;
 11using Renci.SshNet.Channels;
 12using Renci.SshNet.Common;
 13using Renci.SshNet.Compression;
 14using Renci.SshNet.Connection;
 15using Renci.SshNet.Messages;
 16using Renci.SshNet.Messages.Authentication;
 17using Renci.SshNet.Messages.Connection;
 18using Renci.SshNet.Messages.Transport;
 19using Renci.SshNet.Security;
 20using Renci.SshNet.Security.Cryptography;
 21
 22namespace Renci.SshNet
 23{
 24    /// <summary>
 25    /// Provides functionality to connect and interact with SSH server.
 26    /// </summary>
 27    public class Session : ISession
 28    {
 29        internal const byte CarriageReturn = 0x0d;
 30        internal const byte LineFeed = 0x0a;
 31
 32        /// <summary>
 33        /// Specifies an infinite waiting period.
 34        /// </summary>
 35        /// <remarks>
 36        /// The value of this field is <c>-1</c>.
 37        /// </remarks>
 38        internal const int Infinite = -1;
 39
 40        /// <summary>
 41        /// Specifies maximum packet size defined by the protocol.
 42        /// </summary>
 43        /// <value>
 44        /// 68536 (64 KB + 3000 bytes).
 45        /// </value>
 46        internal const int MaximumSshPacketSize = LocalChannelDataPacketSize + 3000;
 47
 48        /// <summary>
 49        /// Holds the initial local window size for the channels.
 50        /// </summary>
 51        /// <value>
 52        /// 2147483647 (2^31 - 1) bytes.
 53        /// </value>
 54        /// <remarks>
 55        /// We currently do not define a maximum (remote) window size.
 56        /// </remarks>
 57        private const int InitialLocalWindowSize = 0x7FFFFFFF;
 58
 59        /// <summary>
 60        /// Holds the maximum size of channel data packets that we receive.
 61        /// </summary>
 62        /// <value>
 63        /// 64 KB.
 64        /// </value>
 65        /// <remarks>
 66        /// <para>
 67        /// This is the maximum size (in bytes) we support for the data (payload) of a
 68        /// <c>SSH_MSG_CHANNEL_DATA</c> message we receive.
 69        /// </para>
 70        /// <para>
 71        /// We currently do not enforce this limit.
 72        /// </para>
 73        /// </remarks>
 74        private const int LocalChannelDataPacketSize = 1024 * 64;
 75
 76        /// <summary>
 77        /// Specifies an infinite waiting period.
 78        /// </summary>
 79        /// <remarks>
 80        /// The value of this field is <c>-1</c> millisecond.
 81        /// </remarks>
 482        internal static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, -1);
 83
 84        /// <summary>
 85        /// Controls how many authentication attempts can take place at the same time.
 86        /// </summary>
 87        /// <remarks>
 88        /// Some server may restrict number to prevent authentication attacks.
 89        /// </remarks>
 490        private static readonly SemaphoreLight AuthenticationConnection = new SemaphoreLight(3);
 91
 92        /// <summary>
 93        /// Holds the factory to use for creating new services.
 94        /// </summary>
 95        private readonly IServiceFactory _serviceFactory;
 96        private readonly ISocketFactory _socketFactory;
 97
 98        /// <summary>
 99        /// Holds an object that is used to ensure only a single thread can read from
 100        /// <see cref="_socket"/> at any given time.
 101        /// </summary>
 1946102        private readonly object _socketReadLock = new object();
 103
 104        /// <summary>
 105        /// Holds an object that is used to ensure only a single thread can write to
 106        /// <see cref="_socket"/> at any given time.
 107        /// </summary>
 108        /// <remarks>
 109        /// This is also used to ensure that <see cref="_outboundPacketSequence"/> is
 110        /// incremented atomatically.
 111        /// </remarks>
 1946112        private readonly object _socketWriteLock = new object();
 113
 114        /// <summary>
 115        /// Holds an object that is used to ensure only a single thread can dispose
 116        /// <see cref="_socket"/> at any given time.
 117        /// </summary>
 118        /// <remarks>
 119        /// This is also used to ensure that <see cref="_socket"/> will not be disposed
 120        /// while performing a given operation or set of operations on <see cref="_socket"/>.
 121        /// </remarks>
 1946122        private readonly object _socketDisposeLock = new object();
 123
 124        /// <summary>
 125        /// Holds an object that is used to ensure only a single thread can connect
 126        /// and lazy initialize the <see cref="SessionSemaphore"/> at any given time.
 127        /// </summary>
 1946128        private readonly object _connectAndLazySemaphoreInitLock = new object();
 129
 130        /// <summary>
 131        /// Holds metadata about session messages.
 132        /// </summary>
 133        private SshMessageFactory _sshMessageFactory;
 134
 135        /// <summary>
 136        /// Holds a <see cref="WaitHandle"/> that is signaled when the message listener loop has completed.
 137        /// </summary>
 138        private ManualResetEvent _messageListenerCompleted;
 139
 140        /// <summary>
 141        /// Specifies outbound packet number.
 142        /// </summary>
 143        private volatile uint _outboundPacketSequence;
 144
 145        /// <summary>
 146        /// Specifies incoming packet number.
 147        /// </summary>
 148        private uint _inboundPacketSequence;
 149
 150        /// <summary>
 151        /// WaitHandle to signal that last service request was accepted.
 152        /// </summary>
 1946153        private EventWaitHandle _serviceAccepted = new AutoResetEvent(initialState: false);
 154
 155        /// <summary>
 156        /// WaitHandle to signal that exception was thrown by another thread.
 157        /// </summary>
 1946158        private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(initialState: false);
 159
 160        /// <summary>
 161        /// WaitHandle to signal that key exchange was completed.
 162        /// </summary>
 1946163        private EventWaitHandle _keyExchangeCompletedWaitHandle = new ManualResetEvent(initialState: false);
 164
 165        /// <summary>
 166        /// WaitHandle to signal that key exchange is in progress.
 167        /// </summary>
 168        private bool _keyExchangeInProgress;
 169
 170        /// <summary>
 171        /// Exception that need to be thrown by waiting thread.
 172        /// </summary>
 173        private Exception _exception;
 174
 175        /// <summary>
 176        /// Specifies whether connection is authenticated.
 177        /// </summary>
 178        private bool _isAuthenticated;
 179
 180        /// <summary>
 181        /// Specifies whether user issued Disconnect command or not.
 182        /// </summary>
 183        private bool _isDisconnecting;
 184
 185        private IKeyExchange _keyExchange;
 186
 187        private HashAlgorithm _serverMac;
 188
 189        private HashAlgorithm _clientMac;
 190
 191        private Cipher _clientCipher;
 192
 193        private Cipher _serverCipher;
 194
 195        private Compressor _serverDecompression;
 196
 197        private Compressor _clientCompression;
 198
 199        private SemaphoreLight _sessionSemaphore;
 200
 201        private bool _isDisconnectMessageSent;
 202
 203        private uint _nextChannelNumber;
 204
 205        /// <summary>
 206        /// Holds connection socket.
 207        /// </summary>
 208        private Socket _socket;
 209
 210        /// <summary>
 211        /// Gets the session semaphore that controls session channels.
 212        /// </summary>
 213        /// <value>
 214        /// The session semaphore.
 215        /// </value>
 216        public SemaphoreLight SessionSemaphore
 217        {
 218            get
 3513219            {
 3513220                if (_sessionSemaphore is null)
 1145221                {
 1145222                    lock (_connectAndLazySemaphoreInitLock)
 1145223                    {
 1145224                        _sessionSemaphore ??= new SemaphoreLight(ConnectionInfo.MaxSessions);
 1145225                    }
 1145226                }
 227
 3513228                return _sessionSemaphore;
 3513229            }
 230        }
 231
 232        /// <summary>
 233        /// Gets the next channel number.
 234        /// </summary>
 235        /// <value>
 236        /// The next channel number.
 237        /// </value>
 238        private uint NextChannelNumber
 239        {
 240            get
 1709241            {
 242                uint result;
 243
 1709244                lock (_connectAndLazySemaphoreInitLock)
 1709245                {
 1709246                    result = _nextChannelNumber++;
 1709247                }
 248
 1709249                return result;
 1709250            }
 251        }
 252
 253        /// <summary>
 254        /// Gets a value indicating whether the session is connected.
 255        /// </summary>
 256        /// <value>
 257        /// <see langword="true"/> if the session is connected; otherwise, <see langword="false"/>.
 258        /// </value>
 259        /// <remarks>
 260        /// This methods returns <see langword="true"/> in all but the following cases:
 261        /// <list type="bullet">
 262        ///     <item>
 263        ///         <description>The <see cref="Session"/> is disposed.</description>
 264        ///     </item>
 265        ///     <item>
 266        ///         <description>The <c>SSH_MSG_DISCONNECT</c> message - which is used to disconnect from the server - h
 267        ///     </item>
 268        ///     <item>
 269        ///         <description>The client has not been authenticated successfully.</description>
 270        ///     </item>
 271        ///     <item>
 272        ///         <description>The listener thread - which is used to receive messages from the server - has stopped.<
 273        ///     </item>
 274        ///     <item>
 275        ///         <description>The socket used to communicate with the server is no longer connected.</description>
 276        ///     </item>
 277        /// </list>
 278        /// </remarks>
 279        public bool IsConnected
 280        {
 281            get
 10744282            {
 10744283                if (_disposed || _isDisconnectMessageSent || !_isAuthenticated)
 5926284                {
 5926285                    return false;
 286                }
 287
 4818288                if (_messageListenerCompleted is null || _messageListenerCompleted.WaitOne(0))
 428289                {
 428290                    return false;
 291                }
 292
 4390293                return IsSocketConnected();
 10744294            }
 295        }
 296
 297        /// <summary>
 298        /// Gets the session id.
 299        /// </summary>
 300        /// <value>
 301        /// The session id, or <see langword="null"/> if the client has not been authenticated.
 302        /// </value>
 142352303        public byte[] SessionId { get; private set; }
 304
 305        private Message _clientInitMessage;
 306
 307        /// <summary>
 308        /// Gets the client init message.
 309        /// </summary>
 310        /// <value>The client init message.</value>
 311        public Message ClientInitMessage
 312        {
 313            get
 2398314            {
 2398315                _clientInitMessage ??= new KeyExchangeInitMessage
 2398316                    {
 2398317                        KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(),
 2398318                        ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(),
 2398319                        EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(),
 2398320                        EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(),
 2398321                        MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(),
 2398322                        MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(),
 2398323                        CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(),
 2398324                        CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(),
 2398325                        LanguagesClientToServer = new[] { string.Empty },
 2398326                        LanguagesServerToClient = new[] { string.Empty },
 2398327                        FirstKexPacketFollows = false,
 2398328                        Reserved = 0
 2398329                    };
 330
 2398331                return _clientInitMessage;
 2398332            }
 333        }
 334
 335        /// <summary>
 336        /// Gets the server version string.
 337        /// </summary>
 338        /// <value>
 339        /// The server version.
 340        /// </value>
 4226341        public string ServerVersion { get; private set; }
 342
 343        /// <summary>
 344        /// Gets the client version string.
 345        /// </summary>
 346        /// <value>
 347        /// The client version.
 348        /// </value>
 9213349        public string ClientVersion { get; private set; }
 350
 351        /// <summary>
 352        /// Gets the connection info.
 353        /// </summary>
 354        /// <value>
 355        /// The connection info.
 356        /// </value>
 76986357        public ConnectionInfo ConnectionInfo { get; private set; }
 358
 359        /// <summary>
 360        /// Occurs when an error occurred.
 361        /// </summary>
 362        public event EventHandler<ExceptionEventArgs> ErrorOccured;
 363
 364        /// <summary>
 365        /// Occurs when session has been disconnected from the server.
 366        /// </summary>
 367        public event EventHandler<EventArgs> Disconnected;
 368
 369        /// <summary>
 370        /// Occurs when server identification received.
 371        /// </summary>
 372        public event EventHandler<SshIdentificationEventArgs> ServerIdentificationReceived;
 373
 374        /// <summary>
 375        /// Occurs when host key received.
 376        /// </summary>
 377        public event EventHandler<HostKeyEventArgs> HostKeyReceived;
 378
 379        /// <summary>
 380        /// Occurs when <see cref="BannerMessage"/> message is received from the server.
 381        /// </summary>
 382        public event EventHandler<MessageEventArgs<BannerMessage>> UserAuthenticationBannerReceived;
 383
 384        /// <summary>
 385        /// Occurs when <see cref="InformationRequestMessage"/> message is received from the server.
 386        /// </summary>
 387        internal event EventHandler<MessageEventArgs<InformationRequestMessage>> UserAuthenticationInformationRequestRec
 388
 389        /// <summary>
 390        /// Occurs when <see cref="PasswordChangeRequiredMessage"/> message is received from the server.
 391        /// </summary>
 392        internal event EventHandler<MessageEventArgs<PasswordChangeRequiredMessage>> UserAuthenticationPasswordChangeReq
 393
 394        /// <summary>
 395        /// Occurs when <see cref="PublicKeyMessage"/> message is received from the server.
 396        /// </summary>
 397        internal event EventHandler<MessageEventArgs<PublicKeyMessage>> UserAuthenticationPublicKeyReceived;
 398
 399        /// <summary>
 400        /// Occurs when <see cref="KeyExchangeDhGroupExchangeGroup"/> message is received from the server.
 401        /// </summary>
 402        internal event EventHandler<MessageEventArgs<KeyExchangeDhGroupExchangeGroup>> KeyExchangeDhGroupExchangeGroupRe
 403
 404        /// <summary>
 405        /// Occurs when <see cref="KeyExchangeDhGroupExchangeReply"/> message is received from the server.
 406        /// </summary>
 407        internal event EventHandler<MessageEventArgs<KeyExchangeDhGroupExchangeReply>> KeyExchangeDhGroupExchangeReplyRe
 408
 409        /// <summary>
 410        /// Occurs when <see cref="DisconnectMessage"/> message received
 411        /// </summary>
 412        internal event EventHandler<MessageEventArgs<DisconnectMessage>> DisconnectReceived;
 413
 414        /// <summary>
 415        /// Occurs when <see cref="IgnoreMessage"/> message received
 416        /// </summary>
 417        internal event EventHandler<MessageEventArgs<IgnoreMessage>> IgnoreReceived;
 418
 419        /// <summary>
 420        /// Occurs when <see cref="UnimplementedMessage"/> message received
 421        /// </summary>
 422        internal event EventHandler<MessageEventArgs<UnimplementedMessage>> UnimplementedReceived;
 423
 424        /// <summary>
 425        /// Occurs when <see cref="DebugMessage"/> message received
 426        /// </summary>
 427        internal event EventHandler<MessageEventArgs<DebugMessage>> DebugReceived;
 428
 429        /// <summary>
 430        /// Occurs when <see cref="ServiceRequestMessage"/> message received
 431        /// </summary>
 432        internal event EventHandler<MessageEventArgs<ServiceRequestMessage>> ServiceRequestReceived;
 433
 434        /// <summary>
 435        /// Occurs when <see cref="ServiceAcceptMessage"/> message received
 436        /// </summary>
 437        internal event EventHandler<MessageEventArgs<ServiceAcceptMessage>> ServiceAcceptReceived;
 438
 439        /// <summary>
 440        /// Occurs when <see cref="KeyExchangeInitMessage"/> message received
 441        /// </summary>
 442        internal event EventHandler<MessageEventArgs<KeyExchangeInitMessage>> KeyExchangeInitReceived;
 443
 444        /// <summary>
 445        /// Occurs when a <see cref="KeyExchangeDhReplyMessage"/> message is received from the SSH server.
 446        /// </summary>
 447        internal event EventHandler<MessageEventArgs<KeyExchangeDhReplyMessage>> KeyExchangeDhReplyMessageReceived;
 448
 449        /// <summary>
 450        /// Occurs when a <see cref="KeyExchangeEcdhReplyMessage"/> message is received from the SSH server.
 451        /// </summary>
 452        internal event EventHandler<MessageEventArgs<KeyExchangeEcdhReplyMessage>> KeyExchangeEcdhReplyMessageReceived;
 453
 454        /// <summary>
 455        /// Occurs when <see cref="NewKeysMessage"/> message received
 456        /// </summary>
 457        internal event EventHandler<MessageEventArgs<NewKeysMessage>> NewKeysReceived;
 458
 459        /// <summary>
 460        /// Occurs when <see cref="RequestMessage"/> message received
 461        /// </summary>
 462        internal event EventHandler<MessageEventArgs<RequestMessage>> UserAuthenticationRequestReceived;
 463
 464        /// <summary>
 465        /// Occurs when <see cref="FailureMessage"/> message received
 466        /// </summary>
 467        internal event EventHandler<MessageEventArgs<FailureMessage>> UserAuthenticationFailureReceived;
 468
 469        /// <summary>
 470        /// Occurs when <see cref="SuccessMessage"/> message received
 471        /// </summary>
 472        internal event EventHandler<MessageEventArgs<SuccessMessage>> UserAuthenticationSuccessReceived;
 473
 474        /// <summary>
 475        /// Occurs when <see cref="GlobalRequestMessage"/> message received
 476        /// </summary>
 477        internal event EventHandler<MessageEventArgs<GlobalRequestMessage>> GlobalRequestReceived;
 478
 479        /// <summary>
 480        /// Occurs when <see cref="RequestSuccessMessage"/> message received
 481        /// </summary>
 482        public event EventHandler<MessageEventArgs<RequestSuccessMessage>> RequestSuccessReceived;
 483
 484        /// <summary>
 485        /// Occurs when <see cref="RequestFailureMessage"/> message received
 486        /// </summary>
 487        public event EventHandler<MessageEventArgs<RequestFailureMessage>> RequestFailureReceived;
 488
 489        /// <summary>
 490        /// Occurs when <see cref="ChannelOpenMessage"/> message received
 491        /// </summary>
 492        public event EventHandler<MessageEventArgs<ChannelOpenMessage>> ChannelOpenReceived;
 493
 494        /// <summary>
 495        /// Occurs when <see cref="ChannelOpenConfirmationMessage"/> message received
 496        /// </summary>
 497        public event EventHandler<MessageEventArgs<ChannelOpenConfirmationMessage>> ChannelOpenConfirmationReceived;
 498
 499        /// <summary>
 500        /// Occurs when <see cref="ChannelOpenFailureMessage"/> message received
 501        /// </summary>
 502        public event EventHandler<MessageEventArgs<ChannelOpenFailureMessage>> ChannelOpenFailureReceived;
 503
 504        /// <summary>
 505        /// Occurs when <see cref="ChannelWindowAdjustMessage"/> message received
 506        /// </summary>
 507        public event EventHandler<MessageEventArgs<ChannelWindowAdjustMessage>> ChannelWindowAdjustReceived;
 508
 509        /// <summary>
 510        /// Occurs when <see cref="ChannelDataMessage"/> message received
 511        /// </summary>
 512        public event EventHandler<MessageEventArgs<ChannelDataMessage>> ChannelDataReceived;
 513
 514        /// <summary>
 515        /// Occurs when <see cref="ChannelExtendedDataMessage"/> message received
 516        /// </summary>
 517        public event EventHandler<MessageEventArgs<ChannelExtendedDataMessage>> ChannelExtendedDataReceived;
 518
 519        /// <summary>
 520        /// Occurs when <see cref="ChannelEofMessage"/> message received
 521        /// </summary>
 522        public event EventHandler<MessageEventArgs<ChannelEofMessage>> ChannelEofReceived;
 523
 524        /// <summary>
 525        /// Occurs when <see cref="ChannelCloseMessage"/> message received
 526        /// </summary>
 527        public event EventHandler<MessageEventArgs<ChannelCloseMessage>> ChannelCloseReceived;
 528
 529        /// <summary>
 530        /// Occurs when <see cref="ChannelRequestMessage"/> message received
 531        /// </summary>
 532        public event EventHandler<MessageEventArgs<ChannelRequestMessage>> ChannelRequestReceived;
 533
 534        /// <summary>
 535        /// Occurs when <see cref="ChannelSuccessMessage"/> message received
 536        /// </summary>
 537        public event EventHandler<MessageEventArgs<ChannelSuccessMessage>> ChannelSuccessReceived;
 538
 539        /// <summary>
 540        /// Occurs when <see cref="ChannelFailureMessage"/> message received
 541        /// </summary>
 542        public event EventHandler<MessageEventArgs<ChannelFailureMessage>> ChannelFailureReceived;
 543
 544        /// <summary>
 545        /// Initializes a new instance of the <see cref="Session"/> class.
 546        /// </summary>
 547        /// <param name="connectionInfo">The connection info.</param>
 548        /// <param name="serviceFactory">The factory to use for creating new services.</param>
 549        /// <param name="socketFactory">A factory to create <see cref="Socket"/> instances.</param>
 550        /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is <see langword="null"/>.</except
 551        /// <exception cref="ArgumentNullException"><paramref name="serviceFactory"/> is <see langword="null"/>.</except
 552        /// <exception cref="ArgumentNullException"><paramref name="socketFactory"/> is <see langword="null"/>.</excepti
 1946553        internal Session(ConnectionInfo connectionInfo, IServiceFactory serviceFactory, ISocketFactory socketFactory)
 1946554        {
 1946555            if (connectionInfo is null)
 3556            {
 3557                throw new ArgumentNullException(nameof(connectionInfo));
 558            }
 559
 1943560            if (serviceFactory is null)
 3561            {
 3562                throw new ArgumentNullException(nameof(serviceFactory));
 563            }
 564
 1940565            if (socketFactory is null)
 3566            {
 3567                throw new ArgumentNullException(nameof(socketFactory));
 568            }
 569
 1937570            ClientVersion = "SSH-2.0-Renci.SshNet.SshClient.0.0.1";
 1937571            ConnectionInfo = connectionInfo;
 1937572            _serviceFactory = serviceFactory;
 1937573            _socketFactory = socketFactory;
 1937574            _messageListenerCompleted = new ManualResetEvent(initialState: true);
 1937575        }
 576
 577        /// <summary>
 578        /// Connects to the server.
 579        /// </summary>
 580        /// <exception cref="SocketException">Socket connection to the SSH server or proxy server could not be establish
 581        /// <exception cref="SshConnectionException">SSH session could not be established.</exception>
 582        /// <exception cref="SshAuthenticationException">Authentication of SSH session failed.</exception>
 583        /// <exception cref="ProxyException">Failed to establish proxy connection.</exception>
 584        public void Connect()
 1875585        {
 1875586            if (IsConnected)
 0587            {
 0588                return;
 589            }
 590
 591            try
 1875592            {
 1875593                AuthenticationConnection.Wait();
 594
 1875595                if (IsConnected)
 0596                {
 0597                    return;
 598                }
 599
 1875600                lock (_connectAndLazySemaphoreInitLock)
 1875601                {
 602                    // If connected don't connect again
 1875603                    if (IsConnected)
 0604                    {
 0605                        return;
 606                    }
 607
 608                    // Reset connection specific information
 1875609                    Reset();
 610
 611                    // Build list of available messages while connecting
 1875612                    _sshMessageFactory = new SshMessageFactory();
 613
 1875614                    _socket = _serviceFactory.CreateConnector(ConnectionInfo, _socketFactory)
 1875615                                             .Connect(ConnectionInfo);
 616
 1815617                    var serverIdentification = _serviceFactory.CreateProtocolVersionExchange()
 1815618                                                              .Start(ClientVersion, _socket, ConnectionInfo.Timeout);
 619
 620                    // Set connection versions
 1815621                    ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
 1815622                    ConnectionInfo.ClientVersion = ClientVersion;
 623
 1815624                    DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification));
 625
 1815626                    if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equ
 0627                    {
 0628                        throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}'
 0629                                                         DisconnectReason.ProtocolVersionNotSupported);
 630                    }
 631
 1815632                    ServerIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification));
 633
 634                    // Register Transport response messages
 1815635                    RegisterMessage("SSH_MSG_DISCONNECT");
 1815636                    RegisterMessage("SSH_MSG_IGNORE");
 1815637                    RegisterMessage("SSH_MSG_UNIMPLEMENTED");
 1815638                    RegisterMessage("SSH_MSG_DEBUG");
 1815639                    RegisterMessage("SSH_MSG_SERVICE_ACCEPT");
 1815640                    RegisterMessage("SSH_MSG_KEXINIT");
 1815641                    RegisterMessage("SSH_MSG_NEWKEYS");
 642
 643                    // Some server implementations might sent this message first, prior to establishing encryption algor
 1815644                    RegisterMessage("SSH_MSG_USERAUTH_BANNER");
 645
 646                    // Mark the message listener threads as started
 1815647                    _ = _messageListenerCompleted.Reset();
 648
 649                    // Start incoming request listener
 650                    // ToDo: Make message pump async, to not consume a thread for every session
 1815651                    _ = ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
 652
 653                    // Wait for key exchange to be completed
 1815654                    WaitOnHandle(_keyExchangeCompletedWaitHandle);
 655
 656                    // If sessionId is not set then its not connected
 1811657                    if (SessionId is null)
 0658                    {
 0659                        Disconnect();
 0660                        return;
 661                    }
 662
 663                    // Request user authorization service
 1811664                    SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication));
 665
 666                    // Wait for service to be accepted
 1811667                    WaitOnHandle(_serviceAccepted);
 668
 1811669                    if (string.IsNullOrEmpty(ConnectionInfo.Username))
 0670                    {
 0671                        throw new SshException("Username is not specified.");
 672                    }
 673
 674                    // Some servers send a global request immediately after successful authentication
 675                    // Avoid race condition by already enabling SSH_MSG_GLOBAL_REQUEST before authentication
 1811676                    RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
 677
 1811678                    ConnectionInfo.Authenticate(this, _serviceFactory);
 1807679                    _isAuthenticated = true;
 680
 681                    // Register Connection messages
 1807682                    RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
 1807683                    RegisterMessage("SSH_MSG_REQUEST_FAILURE");
 1807684                    RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
 1807685                    RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
 1807686                    RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
 1807687                    RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
 1807688                    RegisterMessage("SSH_MSG_CHANNEL_REQUEST");
 1807689                    RegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
 1807690                    RegisterMessage("SSH_MSG_CHANNEL_FAILURE");
 1807691                    RegisterMessage("SSH_MSG_CHANNEL_DATA");
 1807692                    RegisterMessage("SSH_MSG_CHANNEL_EOF");
 1807693                    RegisterMessage("SSH_MSG_CHANNEL_CLOSE");
 1807694                }
 1807695            }
 696            finally
 1875697            {
 1875698                _ = AuthenticationConnection.Release();
 1875699            }
 1807700        }
 701
 702        /// <summary>
 703        /// Asynchronously connects to the server.
 704        /// </summary>
 705        /// <remarks>
 706        /// Please note this function is NOT thread safe.<br/>
 707        /// The caller SHOULD limit the number of simultaneous connection attempts to a server to a single connection at
 708        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
 709        /// <returns>A <see cref="Task"/> that represents the asynchronous connect operation.</returns>
 710        /// <exception cref="SocketException">Socket connection to the SSH server or proxy server could not be establish
 711        /// <exception cref="SshConnectionException">SSH session could not be established.</exception>
 712        /// <exception cref="SshAuthenticationException">Authentication of SSH session failed.</exception>
 713        /// <exception cref="ProxyException">Failed to establish proxy connection.</exception>
 714        public async Task ConnectAsync(CancellationToken cancellationToken)
 8715        {
 716            // If connected don't connect again
 8717            if (IsConnected)
 0718            {
 0719                return;
 720            }
 721
 722            // Reset connection specific information
 8723            Reset();
 724
 725            // Build list of available messages while connecting
 8726            _sshMessageFactory = new SshMessageFactory();
 727
 8728            _socket = await _serviceFactory.CreateConnector(ConnectionInfo, _socketFactory)
 8729                                        .ConnectAsync(ConnectionInfo, cancellationToken).ConfigureAwait(false);
 730
 2731            var serverIdentification = await _serviceFactory.CreateProtocolVersionExchange()
 2732                                                        .StartAsync(ClientVersion, _socket, cancellationToken).Configure
 733
 734            // Set connection versions
 2735            ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
 2736            ConnectionInfo.ClientVersion = ClientVersion;
 737
 2738            DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification));
 739
 2740            if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.9
 0741            {
 0742                throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not 
 0743                                                    DisconnectReason.ProtocolVersionNotSupported);
 744            }
 745
 2746            ServerIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification));
 747
 748            // Register Transport response messages
 2749            RegisterMessage("SSH_MSG_DISCONNECT");
 2750            RegisterMessage("SSH_MSG_IGNORE");
 2751            RegisterMessage("SSH_MSG_UNIMPLEMENTED");
 2752            RegisterMessage("SSH_MSG_DEBUG");
 2753            RegisterMessage("SSH_MSG_SERVICE_ACCEPT");
 2754            RegisterMessage("SSH_MSG_KEXINIT");
 2755            RegisterMessage("SSH_MSG_NEWKEYS");
 756
 757            // Some server implementations might sent this message first, prior to establishing encryption algorithm
 2758            RegisterMessage("SSH_MSG_USERAUTH_BANNER");
 759
 760            // Mark the message listener threads as started
 2761            _ = _messageListenerCompleted.Reset();
 762
 763            // Start incoming request listener
 764            // ToDo: Make message pump async, to not consume a thread for every session
 2765            _ = ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
 766
 767            // Wait for key exchange to be completed
 2768            WaitOnHandle(_keyExchangeCompletedWaitHandle);
 769
 770            // If sessionId is not set then its not connected
 2771            if (SessionId is null)
 0772            {
 0773                Disconnect();
 0774                return;
 775            }
 776
 777            // Request user authorization service
 2778            SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication));
 779
 780            // Wait for service to be accepted
 2781            WaitOnHandle(_serviceAccepted);
 782
 2783            if (string.IsNullOrEmpty(ConnectionInfo.Username))
 0784            {
 0785                throw new SshException("Username is not specified.");
 786            }
 787
 788            // Some servers send a global request immediately after successful authentication
 789            // Avoid race condition by already enabling SSH_MSG_GLOBAL_REQUEST before authentication
 2790            RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
 791
 2792            ConnectionInfo.Authenticate(this, _serviceFactory);
 2793            _isAuthenticated = true;
 794
 795            // Register Connection messages
 2796            RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
 2797            RegisterMessage("SSH_MSG_REQUEST_FAILURE");
 2798            RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION");
 2799            RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE");
 2800            RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST");
 2801            RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA");
 2802            RegisterMessage("SSH_MSG_CHANNEL_REQUEST");
 2803            RegisterMessage("SSH_MSG_CHANNEL_SUCCESS");
 2804            RegisterMessage("SSH_MSG_CHANNEL_FAILURE");
 2805            RegisterMessage("SSH_MSG_CHANNEL_DATA");
 2806            RegisterMessage("SSH_MSG_CHANNEL_EOF");
 2807            RegisterMessage("SSH_MSG_CHANNEL_CLOSE");
 2808        }
 809
 810        /// <summary>
 811        /// Disconnects from the server.
 812        /// </summary>
 813        /// <remarks>
 814        /// This sends a <b>SSH_MSG_DISCONNECT</b> message to the server, waits for the
 815        /// server to close the socket on its end and subsequently closes the client socket.
 816        /// </remarks>
 817        public void Disconnect()
 2060818        {
 2060819            DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting session.", ToHex(SessionId)));
 820
 821            // send SSH_MSG_DISCONNECT message, clear socket read buffer and dispose it
 2060822            Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client.");
 823
 824            // at this point, we are sure that the listener thread will stop as we've
 825            // disconnected the socket, so lets wait until the message listener thread
 826            // has completed
 2060827            if (_messageListenerCompleted != null)
 2060828            {
 2060829                _ = _messageListenerCompleted.WaitOne();
 2060830            }
 2060831        }
 832
 833        private void Disconnect(DisconnectReason reason, string message)
 2358834        {
 835            // transition to disconnecting state to avoid throwing exceptions while cleaning up, and to
 836            // ensure any exceptions that are raised do not overwrite the exception that is set
 2358837            _isDisconnecting = true;
 838
 839            // send disconnect message to the server if the connection is still open
 840            // and the disconnect message has not yet been sent
 841            //
 842            // note that this should also cause the listener loop to be interrupted as
 843            // the server should respond by closing the socket
 2358844            if (IsConnected)
 1430845            {
 1430846                TrySendDisconnect(reason, message);
 1430847            }
 848
 849            // disconnect socket, and dispose it
 2358850            SocketDisconnectAndDispose();
 2358851        }
 852
 853        /// <summary>
 854        /// Waits for the specified handle or the exception handle for the receive thread
 855        /// to signal within the connection timeout.
 856        /// </summary>
 857        /// <param name="waitHandle">The wait handle.</param>
 858        /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity chec
 859        /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is n
 860        /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exc
 861        /// <remarks>
 862        /// When neither handles are signaled in time and the session is not closing, then the
 863        /// session is disconnected.
 864        /// </remarks>
 865        void ISession.WaitOnHandle(WaitHandle waitHandle)
 3614866        {
 3614867            WaitOnHandle(waitHandle, ConnectionInfo.Timeout);
 3581868        }
 869
 870        /// <summary>
 871        /// Waits for the specified handle or the exception handle for the receive thread
 872        /// to signal within the specified timeout.
 873        /// </summary>
 874        /// <param name="waitHandle">The wait handle.</param>
 875        /// <param name="timeout">The time to wait for any of the handles to become signaled.</param>
 876        /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity chec
 877        /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is n
 878        /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exc
 879        /// <remarks>
 880        /// When neither handles are signaled in time and the session is not closing, then the
 881        /// session is disconnected.
 882        /// </remarks>
 883        void ISession.WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
 24884        {
 24885            WaitOnHandle(waitHandle, timeout);
 0886        }
 887
 888        /// <summary>
 889        /// Waits for the specified <seec ref="WaitHandle"/> to receive a signal, using a <see cref="TimeSpan"/>
 890        /// to specify the time interval.
 891        /// </summary>
 892        /// <param name="waitHandle">The <see cref="WaitHandle"/> that should be signaled.</param>
 893        /// <param name="timeout">A <see cref="TimeSpan"/> that represents the number of milliseconds to wait, or a <see
 894        /// <returns>
 895        /// A <see cref="WaitResult"/>.
 896        /// </returns>
 897        WaitResult ISession.TryWait(WaitHandle waitHandle, TimeSpan timeout)
 1744898        {
 1744899            return TryWait(waitHandle, timeout, out _);
 1738900        }
 901
 902        /// <summary>
 903        /// Waits for the specified <seec ref="WaitHandle"/> to receive a signal, using a <see cref="TimeSpan"/>
 904        /// to specify the time interval.
 905        /// </summary>
 906        /// <param name="waitHandle">The <see cref="WaitHandle"/> that should be signaled.</param>
 907        /// <param name="timeout">A <see cref="TimeSpan"/> that represents the number of milliseconds to wait, or a <see
 908        /// <param name="exception">When this method returns <see cref="WaitResult.Failed"/>, contains the <see cref="Ex
 909        /// <returns>
 910        /// A <see cref="WaitResult"/>.
 911        /// </returns>
 912        WaitResult ISession.TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception)
 42913        {
 42914            return TryWait(waitHandle, timeout, out exception);
 36915        }
 916
 917        /// <summary>
 918        /// Waits for the specified <seec ref="WaitHandle"/> to receive a signal, using a <see cref="TimeSpan"/>
 919        /// to specify the time interval.
 920        /// </summary>
 921        /// <param name="waitHandle">The <see cref="WaitHandle"/> that should be signaled.</param>
 922        /// <param name="timeout">A <see cref="TimeSpan"/> that represents the number of milliseconds to wait, or a <see
 923        /// <param name="exception">When this method returns <see cref="WaitResult.Failed"/>, contains the <see cref="Ex
 924        /// <returns>
 925        /// A <see cref="WaitResult"/>.
 926        /// </returns>
 927        private WaitResult TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception)
 1786928        {
 1786929            if (waitHandle is null)
 12930            {
 12931                throw new ArgumentNullException(nameof(waitHandle));
 932            }
 933
 1774934            var waitHandles = new[]
 1774935                {
 1774936                    _exceptionWaitHandle,
 1774937                    _messageListenerCompleted,
 1774938                    waitHandle
 1774939                };
 940
 1774941            switch (WaitHandle.WaitAny(waitHandles, timeout))
 942            {
 943                case 0:
 42944                    if (_exception is SshConnectionException)
 36945                    {
 36946                        exception = null;
 36947                        return WaitResult.Disconnected;
 948                    }
 949
 6950                    exception = _exception;
 6951                    return WaitResult.Failed;
 952                case 1:
 18953                    exception = null;
 18954                    return WaitResult.Disconnected;
 955                case 2:
 1708956                    exception = null;
 1708957                    return WaitResult.Success;
 958                case WaitHandle.WaitTimeout:
 6959                    exception = null;
 6960                    return WaitResult.TimedOut;
 961                default:
 0962                    throw new InvalidOperationException("Unexpected result.");
 963            }
 1774964        }
 965
 966        /// <summary>
 967        /// Waits for the specified handle or the exception handle for the receive thread
 968        /// to signal within the connection timeout.
 969        /// </summary>
 970        /// <param name="waitHandle">The wait handle.</param>
 971        /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity chec
 972        /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is n
 973        /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exc
 974        /// <remarks>
 975        /// When neither handles are signaled in time and the session is not closing, then the
 976        /// session is disconnected.
 977        /// </remarks>
 978        internal void WaitOnHandle(WaitHandle waitHandle)
 7376979        {
 7376980            WaitOnHandle(waitHandle, ConnectionInfo.Timeout);
 7362981        }
 982
 983        /// <summary>
 984        /// Waits for the specified handle or the exception handle for the receive thread
 985        /// to signal within the specified timeout.
 986        /// </summary>
 987        /// <param name="waitHandle">The wait handle.</param>
 988        /// <param name="timeout">The time to wait for any of the handles to become signaled.</param>
 989        /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity chec
 990        /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is n
 991        /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exc
 992        internal void WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
 11023993        {
 11023994            if (waitHandle is null)
 33995            {
 33996                throw new ArgumentNullException(nameof(waitHandle));
 997            }
 998
 10990999            var waitHandles = new[]
 109901000                {
 109901001                    _exceptionWaitHandle,
 109901002                    _messageListenerCompleted,
 109901003                    waitHandle
 109901004                };
 1005
 109901006            var signaledElement = WaitHandle.WaitAny(waitHandles, timeout);
 109901007            switch (signaledElement)
 1008            {
 1009                case 0:
 411010                    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(_exception).Throw();
 01011                    break;
 1012                case 1:
 61013                    throw new SshConnectionException("Client not connected.");
 1014                case 2:
 1015                    // Specified waithandle was signaled
 109431016                    break;
 1017                case WaitHandle.WaitTimeout:
 1018                    // when the session is disconnecting, a timeout is likely when no
 1019                    // network connectivity is available; depending on the configured
 1020                    // timeout either the WaitAny times out first or a SocketException
 1021                    // detailing a timeout thrown hereby completing the listener thread
 1022                    // (which makes us end up in case 1). Either way, we do not want to
 1023                    // report an exception to the client when we're disconnecting anyway
 01024                    if (!_isDisconnecting)
 01025                    {
 01026                        throw new SshOperationTimeoutException("Session operation has timed out");
 1027                    }
 1028
 01029                    break;
 1030                default:
 01031                    throw new SshException($"Unexpected element '{signaledElement.ToString(CultureInfo.InvariantCulture)
 1032            }
 109431033        }
 1034
 1035        /// <summary>
 1036        /// Sends a message to the server.
 1037        /// </summary>
 1038        /// <param name="message">The message to send.</param>
 1039        /// <exception cref="SshConnectionException">The client is not connected.</exception>
 1040        /// <exception cref="SshOperationTimeoutException">The operation timed out.</exception>
 1041        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the p
 1042        internal void SendMessage(Message message)
 531261043        {
 531261044            if (!_socket.CanWrite())
 861045            {
 861046                throw new SshConnectionException("Client not connected.");
 1047            }
 1048
 530401049            if (_keyExchangeInProgress && message is not IKeyExchangedAllowed)
 6301050            {
 1051                // Wait for key exchange to be completed
 6301052                WaitOnHandle(_keyExchangeCompletedWaitHandle);
 6301053            }
 1054
 530401055            DiagnosticAbstraction.Log(string.Format("[{0}] Sending message '{1}' to server: '{2}'.", ToHex(SessionId), m
 1056
 530401057            var paddingMultiplier = _clientCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize);
 530401058            var packetData = message.GetPacket(paddingMultiplier, _clientCompression);
 1059
 1060            // take a write lock to ensure the outbound packet sequence number is incremented
 1061            // atomically, and only after the packet has actually been sent
 530401062            lock (_socketWriteLock)
 530401063            {
 530401064                byte[] hash = null;
 530401065                var packetDataOffset = 4; // first four bytes are reserved for outbound packet sequence
 1066
 530401067                if (_clientMac != null)
 485631068                {
 1069                    // write outbound packet sequence to start of packet data
 485631070                    Pack.UInt32ToBigEndian(_outboundPacketSequence, packetData);
 1071
 1072                    // calculate packet hash
 485631073                    hash = _clientMac.ComputeHash(packetData);
 485631074                }
 1075
 1076                // Encrypt packet data
 530401077                if (_clientCipher != null)
 485631078                {
 485631079                    packetData = _clientCipher.Encrypt(packetData, packetDataOffset, packetData.Length - packetDataOffse
 485631080                    packetDataOffset = 0;
 485631081                }
 1082
 530401083                if (packetData.Length > MaximumSshPacketSize)
 01084                {
 01085                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Packet is too big. Ma
 1086                }
 1087
 530401088                var packetLength = packetData.Length - packetDataOffset;
 530401089                if (hash is null)
 44771090                {
 44771091                    SendPacket(packetData, packetDataOffset, packetLength);
 44351092                }
 1093                else
 485631094                {
 485631095                    var data = new byte[packetLength + hash.Length];
 485631096                    Buffer.BlockCopy(packetData, packetDataOffset, data, 0, packetLength);
 485631097                    Buffer.BlockCopy(hash, 0, data, packetLength, hash.Length);
 485631098                    SendPacket(data, 0, data.Length);
 485631099                }
 1100
 1101                // increment the packet sequence number only after we're sure the packet has
 1102                // been sent; even though it's only used for the MAC, it needs to be incremented
 1103                // for each package sent.
 1104                //
 1105                // the server will use it to verify the data integrity, and as such the order in
 1106                // which messages are sent must follow the outbound packet sequence number
 529981107                _outboundPacketSequence++;
 529981108            }
 529981109        }
 1110
 1111        /// <summary>
 1112        /// Sends an SSH packet to the server.
 1113        /// </summary>
 1114        /// <param name="packet">A byte array containing the packet to send.</param>
 1115        /// <param name="offset">The offset of the packet.</param>
 1116        /// <param name="length">The length of the packet.</param>
 1117        /// <exception cref="SshConnectionException">Client is not connected to the server.</exception>
 1118        /// <remarks>
 1119        /// <para>
 1120        /// The send is performed in a dispose lock to avoid <see cref="NullReferenceException"/>
 1121        /// and/or <see cref="ObjectDisposedException"/> when sending the packet.
 1122        /// </para>
 1123        /// <para>
 1124        /// This method is only to be used when the connection is established, as the locking
 1125        /// overhead is not required while establising the connection.
 1126        /// </para>
 1127        /// </remarks>
 1128        private void SendPacket(byte[] packet, int offset, int length)
 530401129        {
 530401130            lock (_socketDisposeLock)
 530401131            {
 530401132                if (!_socket.IsConnected())
 421133                {
 421134                    throw new SshConnectionException("Client not connected.");
 1135                }
 1136
 529981137                SocketAbstraction.Send(_socket, packet, offset, length);
 529981138            }
 529981139        }
 1140
 1141        /// <summary>
 1142        /// Sends a message to the server.
 1143        /// </summary>
 1144        /// <param name="message">The message to send.</param>
 1145        /// <returns>
 1146        /// <see langword="true"/> if the message was sent to the server; otherwise, <see langword="false"/>.
 1147        /// </returns>
 1148        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the p
 1149        /// <remarks>
 1150        /// This methods returns <see langword="false"/> when the attempt to send the message results in a
 1151        /// <see cref="SocketException"/> or a <see cref="SshException"/>.
 1152        /// </remarks>
 1153        private bool TrySendMessage(Message message)
 41531154        {
 1155            try
 41531156            {
 41531157                SendMessage(message);
 40811158                return true;
 1159            }
 721160            catch (SshException ex)
 721161            {
 721162                DiagnosticAbstraction.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message
 721163                return false;
 1164            }
 01165            catch (SocketException ex)
 01166            {
 01167                DiagnosticAbstraction.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message
 01168                return false;
 1169            }
 41531170        }
 1171
 1172        /// <summary>
 1173        /// Receives the message from the server.
 1174        /// </summary>
 1175        /// <returns>
 1176        /// The incoming SSH message, or <see langword="null"/> if the connection with the SSH server was closed.
 1177        /// </returns>
 1178        /// <remarks>
 1179        /// We need no locking here since all messages are read by a single thread.
 1180        /// </remarks>
 1181        private Message ReceiveMessage(Socket socket)
 583881182        {
 1183            // the length of the packet sequence field in bytes
 1184            const int inboundPacketSequenceLength = 4;
 1185
 1186            // The length of the "packet length" field in bytes
 1187            const int packetLengthFieldLength = 4;
 1188
 1189            // The length of the "padding length" field in bytes
 1190            const int paddingLengthFieldLength = 1;
 1191
 1192            // Determine the size of the first block, which is 8 or cipher block size (whichever is larger) bytes
 583881193            var blockSize = _serverCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize);
 1194
 583881195            var serverMacLength = _serverMac != null ? _serverMac.HashSize/8 : 0;
 1196
 1197            byte[] data;
 1198            uint packetLength;
 1199
 1200            // avoid reading from socket while IsSocketConnected is attempting to determine whether the
 1201            // socket is still connected by invoking Socket.Poll(...) and subsequently verifying value of
 1202            // Socket.Available
 583881203            lock (_socketReadLock)
 583881204            {
 1205                // Read first block - which starts with the packet length
 583881206                var firstBlock = new byte[blockSize];
 583881207                if (TrySocketRead(socket, firstBlock, 0, blockSize) == 0)
 451208                {
 1209                    // connection with SSH server was closed
 451210                    return null;
 1211                }
 1212
 583431213                if (_serverCipher != null)
 526161214                {
 526161215                    firstBlock = _serverCipher.Decrypt(firstBlock);
 526161216                }
 1217
 583431218                packetLength = Pack.BigEndianToUInt32(firstBlock);
 1219
 1220                // Test packet minimum and maximum boundaries
 583431221                if (packetLength < Math.Max((byte) 16, blockSize) - 4 || packetLength > MaximumSshPacketSize - 4)
 481222                {
 481223                    throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length: {0}."
 481224                                                     DisconnectReason.ProtocolError);
 1225                }
 1226
 1227                // Determine the number of bytes left to read; We've already read "blockSize" bytes, but the
 1228                // "packet length" field itself - which is 4 bytes - is not included in the length of the packet
 582951229                var bytesToRead = (int) (packetLength - (blockSize - packetLengthFieldLength)) + serverMacLength;
 1230
 1231                // Construct buffer for holding the payload and the inbound packet sequence as we need both in order
 1232                // to generate the hash.
 1233                //
 1234                // The total length of the "data" buffer is an addition of:
 1235                // - inboundPacketSequenceLength (4 bytes)
 1236                // - packetLength
 1237                // - serverMacLength
 1238                //
 1239                // We include the inbound packet sequence to allow us to have the the full SSH packet in a single
 1240                // byte[] for the purpose of calculating the client hash. Room for the server MAC is foreseen
 1241                // to read the packet including server MAC in a single pass (except for the initial block).
 582951242                data = new byte[bytesToRead + blockSize + inboundPacketSequenceLength];
 582951243                Pack.UInt32ToBigEndian(_inboundPacketSequence, data);
 582951244                Buffer.BlockCopy(firstBlock, 0, data, inboundPacketSequenceLength, firstBlock.Length);
 1245
 582951246                if (bytesToRead > 0)
 582951247                {
 582951248                    if (TrySocketRead(socket, data, blockSize + inboundPacketSequenceLength, bytesToRead) == 0)
 01249                    {
 01250                        return null;
 1251                    }
 582951252                }
 582951253            }
 1254
 582951255            if (_serverCipher != null)
 526161256            {
 526161257                var numberOfBytesToDecrypt = data.Length - (blockSize + inboundPacketSequenceLength + serverMacLength);
 526161258                if (numberOfBytesToDecrypt > 0)
 470761259                {
 470761260                    var decryptedData = _serverCipher.Decrypt(data, blockSize + inboundPacketSequenceLength, numberOfByt
 470761261                    Buffer.BlockCopy(decryptedData, 0, data, blockSize + inboundPacketSequenceLength, decryptedData.Leng
 470761262                }
 526161263            }
 1264
 582951265            var paddingLength = data[inboundPacketSequenceLength + packetLengthFieldLength];
 582951266            var messagePayloadLength = (int) packetLength - paddingLength - paddingLengthFieldLength;
 582951267            var messagePayloadOffset = inboundPacketSequenceLength + packetLengthFieldLength + paddingLengthFieldLength;
 1268
 1269            // validate message against MAC
 582951270            if (_serverMac != null)
 526161271            {
 526161272                var clientHash = _serverMac.ComputeHash(data, 0, data.Length - serverMacLength);
 526161273                var serverHash = data.Take(data.Length - serverMacLength, serverMacLength);
 1274
 1275                // TODO Add IsEqualTo overload that takes left+right index and number of bytes to compare.
 1276                // TODO That way we can eliminate the extra allocation of the Take above.
 526161277                if (!serverHash.IsEqualTo(clientHash))
 01278                {
 01279                    throw new SshConnectionException("MAC error", DisconnectReason.MacError);
 1280                }
 526161281            }
 1282
 582951283            if (_serverDecompression != null)
 01284            {
 01285                data = _serverDecompression.Decompress(data, messagePayloadOffset, messagePayloadLength);
 1286
 1287                // Data now only contains the decompressed payload, and as such the offset is reset to zero
 01288                messagePayloadOffset = 0;
 1289
 1290                // The length of the payload is now the complete decompressed content
 01291                messagePayloadLength = data.Length;
 01292            }
 1293
 582951294            _inboundPacketSequence++;
 1295
 582951296            return LoadMessage(data, messagePayloadOffset, messagePayloadLength);
 582981297        }
 1298
 1299        private void TrySendDisconnect(DisconnectReason reasonCode, string message)
 14301300        {
 14301301            var disconnectMessage = new DisconnectMessage(reasonCode, message);
 1302
 1303            // Send the disconnect message, but ignore the outcome
 14301304            _ = TrySendMessage(disconnectMessage);
 1305
 1306            // Mark disconnect message sent regardless of whether the send sctually succeeded
 14301307            _isDisconnectMessageSent = true;
 14301308        }
 1309
 1310        /// <summary>
 1311        /// Called when <see cref="DisconnectMessage"/> received.
 1312        /// </summary>
 1313        /// <param name="message"><see cref="DisconnectMessage"/> message.</param>
 1314        internal void OnDisconnectReceived(DisconnectMessage message)
 1891315        {
 1891316            DiagnosticAbstraction.Log(string.Format("[{0}] Disconnect received: {1} {2}.", ToHex(SessionId), message.Rea
 1317
 1318            // transition to disconnecting state to avoid throwing exceptions while cleaning up, and to
 1319            // ensure any exceptions that are raised do not overwrite the SshConnectionException that we
 1320            // set below
 1891321            _isDisconnecting = true;
 1322
 1891323            _exception = new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The connection was clos
 1891324            _ = _exceptionWaitHandle.Set();
 1325
 1891326            DisconnectReceived?.Invoke(this, new MessageEventArgs<DisconnectMessage>(message));
 1327
 1891328            Disconnected?.Invoke(this, EventArgs.Empty);
 1329
 1330            // disconnect socket, and dispose it
 1891331            SocketDisconnectAndDispose();
 1891332        }
 1333
 1334        /// <summary>
 1335        /// Called when <see cref="IgnoreMessage"/> received.
 1336        /// </summary>
 1337        /// <param name="message"><see cref="IgnoreMessage"/> message.</param>
 1338        internal void OnIgnoreReceived(IgnoreMessage message)
 11339        {
 11340            IgnoreReceived?.Invoke(this, new MessageEventArgs<IgnoreMessage>(message));
 11341        }
 1342
 1343        /// <summary>
 1344        /// Called when <see cref="UnimplementedMessage"/> message received.
 1345        /// </summary>
 1346        /// <param name="message"><see cref="UnimplementedMessage"/> message.</param>
 1347        internal void OnUnimplementedReceived(UnimplementedMessage message)
 01348        {
 01349            UnimplementedReceived?.Invoke(this, new MessageEventArgs<UnimplementedMessage>(message));
 01350        }
 1351
 1352        /// <summary>
 1353        /// Called when <see cref="DebugMessage"/> message received.
 1354        /// </summary>
 1355        /// <param name="message"><see cref="DebugMessage"/> message.</param>
 1356        internal void OnDebugReceived(DebugMessage message)
 12801357        {
 12801358            DebugReceived?.Invoke(this, new MessageEventArgs<DebugMessage>(message));
 12801359        }
 1360
 1361        /// <summary>
 1362        /// Called when <see cref="ServiceRequestMessage"/> message received.
 1363        /// </summary>
 1364        /// <param name="message"><see cref="ServiceRequestMessage"/> message.</param>
 1365        internal void OnServiceRequestReceived(ServiceRequestMessage message)
 01366        {
 01367            ServiceRequestReceived?.Invoke(this, new MessageEventArgs<ServiceRequestMessage>(message));
 01368        }
 1369
 1370        /// <summary>
 1371        /// Called when <see cref="ServiceAcceptMessage"/> message received.
 1372        /// </summary>
 1373        /// <param name="message"><see cref="ServiceAcceptMessage"/> message.</param>
 1374        internal void OnServiceAcceptReceived(ServiceAcceptMessage message)
 18131375        {
 18131376            ServiceAcceptReceived?.Invoke(this, new MessageEventArgs<ServiceAcceptMessage>(message));
 1377
 18131378            _ = _serviceAccepted.Set();
 18131379        }
 1380
 1381        internal void OnKeyExchangeDhGroupExchangeGroupReceived(KeyExchangeDhGroupExchangeGroup message)
 61382        {
 61383            KeyExchangeDhGroupExchangeGroupReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhGroupExchangeGroup>(
 61384        }
 1385
 1386        internal void OnKeyExchangeDhGroupExchangeReplyReceived(KeyExchangeDhGroupExchangeReply message)
 61387        {
 61388            KeyExchangeDhGroupExchangeReplyReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhGroupExchangeReply>(
 61389        }
 1390
 1391        /// <summary>
 1392        /// Called when <see cref="KeyExchangeInitMessage"/> message received.
 1393        /// </summary>
 1394        /// <param name="message"><see cref="KeyExchangeInitMessage"/> message.</param>
 1395        internal void OnKeyExchangeInitReceived(KeyExchangeInitMessage message)
 18141396        {
 18141397            _keyExchangeInProgress = true;
 1398
 18141399            _ = _keyExchangeCompletedWaitHandle.Reset();
 1400
 1401            // Disable messages that are not key exchange related
 18141402            _sshMessageFactory.DisableNonKeyExchangeMessages();
 1403
 18141404            _keyExchange = _serviceFactory.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms,
 18141405                                                             message.KeyExchangeAlgorithms);
 1406
 18141407            ConnectionInfo.CurrentKeyExchangeAlgorithm = _keyExchange.Name;
 1408
 18141409            DiagnosticAbstraction.Log(string.Format("[{0}] Performing {1} key exchange.", ToHex(SessionId), ConnectionIn
 1410
 18141411            _keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived;
 1412
 1413            // Start the algorithm implementation
 18141414            _keyExchange.Start(this, message);
 1415
 18141416            KeyExchangeInitReceived?.Invoke(this, new MessageEventArgs<KeyExchangeInitMessage>(message));
 18141417        }
 1418
 1419        internal void OnKeyExchangeDhReplyMessageReceived(KeyExchangeDhReplyMessage message)
 121420        {
 121421            KeyExchangeDhReplyMessageReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhReplyMessage>(message));
 121422        }
 1423
 1424        internal void OnKeyExchangeEcdhReplyMessageReceived(KeyExchangeEcdhReplyMessage message)
 11811425        {
 11811426            KeyExchangeEcdhReplyMessageReceived?.Invoke(this, new MessageEventArgs<KeyExchangeEcdhReplyMessage>(message)
 11801427        }
 1428
 1429        /// <summary>
 1430        /// Called when <see cref="NewKeysMessage"/> message received.
 1431        /// </summary>
 1432        /// <param name="message"><see cref="NewKeysMessage"/> message.</param>
 1433        internal void OnNewKeysReceived(NewKeysMessage message)
 18131434        {
 1435            // Update sessionId
 18131436            SessionId ??= _keyExchange.ExchangeHash;
 1437
 1438            // Dispose of old ciphers and hash algorithms
 18131439            if (_serverCipher is IDisposable disposableServerCipher)
 01440            {
 01441                disposableServerCipher.Dispose();
 01442            }
 1443
 18131444            if (_clientCipher is IDisposable disposableClientCipher)
 01445            {
 01446                disposableClientCipher.Dispose();
 01447            }
 1448
 18131449            if (_serverMac != null)
 01450            {
 01451                _serverMac.Dispose();
 01452                _serverMac = null;
 01453            }
 1454
 18131455            if (_clientMac != null)
 01456            {
 01457                _clientMac.Dispose();
 01458                _clientMac = null;
 01459            }
 1460
 1461            // Update negotiated algorithms
 18131462            _serverCipher = _keyExchange.CreateServerCipher();
 18131463            _clientCipher = _keyExchange.CreateClientCipher();
 18131464            _serverMac = _keyExchange.CreateServerHash();
 18131465            _clientMac = _keyExchange.CreateClientHash();
 18131466            _clientCompression = _keyExchange.CreateCompressor();
 18131467            _serverDecompression = _keyExchange.CreateDecompressor();
 1468
 1469            // Dispose of old KeyExchange object as it is no longer needed.
 18131470            _keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
 18131471            _keyExchange.Dispose();
 18131472            _keyExchange = null;
 1473
 1474            // Enable activated messages that are not key exchange related
 18131475            _sshMessageFactory.EnableActivatedMessages();
 1476
 18131477            NewKeysReceived?.Invoke(this, new MessageEventArgs<NewKeysMessage>(message));
 1478
 1479            // Signal that key exchange completed
 18131480            _ = _keyExchangeCompletedWaitHandle.Set();
 1481
 18131482            _keyExchangeInProgress = false;
 18131483        }
 1484
 1485        /// <summary>
 1486        /// Called when client is disconnecting from the server.
 1487        /// </summary>
 1488        void ISession.OnDisconnecting()
 11931489        {
 11931490            _isDisconnecting = true;
 11931491        }
 1492
 1493        /// <summary>
 1494        /// Called when <see cref="RequestMessage"/> message received.
 1495        /// </summary>
 1496        /// <param name="message"><see cref="RequestMessage"/> message.</param>
 1497        internal void OnUserAuthenticationRequestReceived(RequestMessage message)
 01498        {
 01499            UserAuthenticationRequestReceived?.Invoke(this, new MessageEventArgs<RequestMessage>(message));
 01500        }
 1501
 1502        /// <summary>
 1503        /// Called when <see cref="FailureMessage"/> message received.
 1504        /// </summary>
 1505        /// <param name="message"><see cref="FailureMessage"/> message.</param>
 1506        internal void OnUserAuthenticationFailureReceived(FailureMessage message)
 12231507        {
 12231508            UserAuthenticationFailureReceived?.Invoke(this, new MessageEventArgs<FailureMessage>(message));
 12231509        }
 1510
 1511        /// <summary>
 1512        /// Called when <see cref="SuccessMessage"/> message received.
 1513        /// </summary>
 1514        /// <param name="message"><see cref="SuccessMessage"/> message.</param>
 1515        internal void OnUserAuthenticationSuccessReceived(SuccessMessage message)
 11941516        {
 11941517            UserAuthenticationSuccessReceived?.Invoke(this, new MessageEventArgs<SuccessMessage>(message));
 11941518        }
 1519
 1520        /// <summary>
 1521        /// Called when <see cref="BannerMessage"/> message received.
 1522        /// </summary>
 1523        /// <param name="message"><see cref="BannerMessage"/> message.</param>
 1524        internal void OnUserAuthenticationBannerReceived(BannerMessage message)
 01525        {
 01526            UserAuthenticationBannerReceived?.Invoke(this, new MessageEventArgs<BannerMessage>(message));
 01527        }
 1528
 1529        /// <summary>
 1530        /// Called when <see cref="InformationRequestMessage"/> message received.
 1531        /// </summary>
 1532        /// <param name="message"><see cref="InformationRequestMessage"/> message.</param>
 1533        internal void OnUserAuthenticationInformationRequestReceived(InformationRequestMessage message)
 91534        {
 91535            UserAuthenticationInformationRequestReceived?.Invoke(this, new MessageEventArgs<InformationRequestMessage>(m
 91536        }
 1537
 1538        internal void OnUserAuthenticationPasswordChangeRequiredReceived(PasswordChangeRequiredMessage message)
 01539        {
 01540            UserAuthenticationPasswordChangeRequiredReceived?.Invoke(this, new MessageEventArgs<PasswordChangeRequiredMe
 01541        }
 1542
 1543        internal void OnUserAuthenticationPublicKeyReceived(PublicKeyMessage message)
 6751544        {
 6751545            UserAuthenticationPublicKeyReceived?.Invoke(this, new MessageEventArgs<PublicKeyMessage>(message));
 6751546        }
 1547
 1548        /// <summary>
 1549        /// Called when <see cref="GlobalRequestMessage"/> message received.
 1550        /// </summary>
 1551        /// <param name="message"><see cref="GlobalRequestMessage"/> message.</param>
 1552        internal void OnGlobalRequestReceived(GlobalRequestMessage message)
 11541553        {
 11541554            GlobalRequestReceived?.Invoke(this, new MessageEventArgs<GlobalRequestMessage>(message));
 11541555        }
 1556
 1557        /// <summary>
 1558        /// Called when <see cref="RequestSuccessMessage"/> message received.
 1559        /// </summary>
 1560        /// <param name="message"><see cref="RequestSuccessMessage"/> message.</param>
 1561        internal void OnRequestSuccessReceived(RequestSuccessMessage message)
 41562        {
 41563            RequestSuccessReceived?.Invoke(this, new MessageEventArgs<RequestSuccessMessage>(message));
 41564        }
 1565
 1566        /// <summary>
 1567        /// Called when <see cref="RequestFailureMessage"/> message received.
 1568        /// </summary>
 1569        /// <param name="message"><see cref="RequestFailureMessage"/> message.</param>
 1570        internal void OnRequestFailureReceived(RequestFailureMessage message)
 01571        {
 01572            RequestFailureReceived?.Invoke(this, new MessageEventArgs<RequestFailureMessage>(message));
 01573        }
 1574
 1575        /// <summary>
 1576        /// Called when <see cref="ChannelOpenMessage"/> message received.
 1577        /// </summary>
 1578        /// <param name="message"><see cref="ChannelOpenMessage"/> message.</param>
 1579        internal void OnChannelOpenReceived(ChannelOpenMessage message)
 21580        {
 21581            ChannelOpenReceived?.Invoke(this, new MessageEventArgs<ChannelOpenMessage>(message));
 21582        }
 1583
 1584        /// <summary>
 1585        /// Called when <see cref="ChannelOpenConfirmationMessage"/> message received.
 1586        /// </summary>
 1587        /// <param name="message"><see cref="ChannelOpenConfirmationMessage"/> message.</param>
 1588        internal void OnChannelOpenConfirmationReceived(ChannelOpenConfirmationMessage message)
 17071589        {
 17071590            ChannelOpenConfirmationReceived?.Invoke(this, new MessageEventArgs<ChannelOpenConfirmationMessage>(message))
 17071591        }
 1592
 1593        /// <summary>
 1594        /// Called when <see cref="ChannelOpenFailureMessage"/> message received.
 1595        /// </summary>
 1596        /// <param name="message"><see cref="ChannelOpenFailureMessage"/> message.</param>
 1597        internal void OnChannelOpenFailureReceived(ChannelOpenFailureMessage message)
 571598        {
 571599            ChannelOpenFailureReceived?.Invoke(this, new MessageEventArgs<ChannelOpenFailureMessage>(message));
 571600        }
 1601
 1602        /// <summary>
 1603        /// Called when <see cref="ChannelWindowAdjustMessage"/> message received.
 1604        /// </summary>
 1605        /// <param name="message"><see cref="ChannelWindowAdjustMessage"/> message.</param>
 1606        internal void OnChannelWindowAdjustReceived(ChannelWindowAdjustMessage message)
 32741607        {
 32741608            ChannelWindowAdjustReceived?.Invoke(this, new MessageEventArgs<ChannelWindowAdjustMessage>(message));
 32741609        }
 1610
 1611        /// <summary>
 1612        /// Called when <see cref="ChannelDataMessage"/> message received.
 1613        /// </summary>
 1614        /// <param name="message"><see cref="ChannelDataMessage"/> message.</param>
 1615        internal void OnChannelDataReceived(ChannelDataMessage message)
 347541616        {
 347541617            ChannelDataReceived?.Invoke(this, new MessageEventArgs<ChannelDataMessage>(message));
 347541618        }
 1619
 1620        /// <summary>
 1621        /// Called when <see cref="ChannelExtendedDataMessage"/> message received.
 1622        /// </summary>
 1623        /// <param name="message"><see cref="ChannelExtendedDataMessage"/> message.</param>
 1624        internal void OnChannelExtendedDataReceived(ChannelExtendedDataMessage message)
 401625        {
 401626            ChannelExtendedDataReceived?.Invoke(this, new MessageEventArgs<ChannelExtendedDataMessage>(message));
 401627        }
 1628
 1629        /// <summary>
 1630        /// Called when <see cref="ChannelCloseMessage"/> message received.
 1631        /// </summary>
 1632        /// <param name="message"><see cref="ChannelCloseMessage"/> message.</param>
 1633        internal void OnChannelEofReceived(ChannelEofMessage message)
 9441634        {
 9441635            ChannelEofReceived?.Invoke(this, new MessageEventArgs<ChannelEofMessage>(message));
 9441636        }
 1637
 1638        /// <summary>
 1639        /// Called when <see cref="ChannelCloseMessage"/> message received.
 1640        /// </summary>
 1641        /// <param name="message"><see cref="ChannelCloseMessage"/> message.</param>
 1642        internal void OnChannelCloseReceived(ChannelCloseMessage message)
 17021643        {
 17021644            ChannelCloseReceived?.Invoke(this, new MessageEventArgs<ChannelCloseMessage>(message));
 17021645        }
 1646
 1647        /// <summary>
 1648        /// Called when <see cref="ChannelRequestMessage"/> message received.
 1649        /// </summary>
 1650        /// <param name="message"><see cref="ChannelRequestMessage"/> message.</param>
 1651        internal void OnChannelRequestReceived(ChannelRequestMessage message)
 16921652        {
 16921653            ChannelRequestReceived?.Invoke(this, new MessageEventArgs<ChannelRequestMessage>(message));
 16921654        }
 1655
 1656        /// <summary>
 1657        /// Called when <see cref="ChannelSuccessMessage"/> message received.
 1658        /// </summary>
 1659        /// <param name="message"><see cref="ChannelSuccessMessage"/> message.</param>
 1660        internal void OnChannelSuccessReceived(ChannelSuccessMessage message)
 17061661        {
 17061662            ChannelSuccessReceived?.Invoke(this, new MessageEventArgs<ChannelSuccessMessage>(message));
 17061663        }
 1664
 1665        /// <summary>
 1666        /// Called when <see cref="ChannelFailureMessage"/> message received.
 1667        /// </summary>
 1668        /// <param name="message"><see cref="ChannelFailureMessage"/> message.</param>
 1669        internal void OnChannelFailureReceived(ChannelFailureMessage message)
 11670        {
 11671            ChannelFailureReceived?.Invoke(this, new MessageEventArgs<ChannelFailureMessage>(message));
 11672        }
 1673
 1674        private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e)
 11991675        {
 11991676            HostKeyReceived?.Invoke(this, e);
 11991677        }
 1678
 1679        /// <summary>
 1680        /// Registers SSH message with the session.
 1681        /// </summary>
 1682        /// <param name="messageName">The name of the message to register with the session.</param>
 1683        public void RegisterMessage(string messageName)
 440751684        {
 440751685            _sshMessageFactory.EnableAndActivateMessage(messageName);
 440751686        }
 1687
 1688        /// <summary>
 1689        /// Unregister SSH message from the session.
 1690        /// </summary>
 1691        /// <param name="messageName">The name of the message to unregister with the session.</param>
 1692        public void UnRegisterMessage(string messageName)
 60121693        {
 60121694            _sshMessageFactory.DisableAndDeactivateMessage(messageName);
 60121695        }
 1696
 1697        /// <summary>
 1698        /// Loads a message from a given buffer.
 1699        /// </summary>
 1700        /// <param name="data">An array of bytes from which to construct the message.</param>
 1701        /// <param name="offset">The zero-based byte offset in <paramref name="data"/> at which to begin reading.</param
 1702        /// <param name="count">The number of bytes to load.</param>
 1703        /// <returns>
 1704        /// A message constructed from <paramref name="data"/>.
 1705        /// </returns>
 1706        /// <exception cref="SshException">The type of the message is not supported.</exception>
 1707        private Message LoadMessage(byte[] data, int offset, int count)
 582951708        {
 582951709            var messageType = data[offset];
 1710
 582951711            var message = _sshMessageFactory.Create(messageType);
 582531712            message.Load(data, offset + 1, count - 1);
 1713
 582531714            DiagnosticAbstraction.Log(string.Format("[{0}] Received message '{1}' from server: '{2}'.", ToHex(SessionId)
 1715
 582531716            return message;
 582531717        }
 1718
 1719        private static string ToHex(byte[] bytes, int offset)
 1192341720        {
 1192341721            var byteCount = bytes.Length - offset;
 1722
 1192341723            var builder = new StringBuilder(bytes.Length * 2);
 1724
 76102601725            for (var i = offset; i < byteCount; i++)
 36858961726            {
 36858961727                var b = bytes[i];
 36858961728                _ = builder.Append(b.ToString("X2"));
 36858961729            }
 1730
 1192341731            return builder.ToString();
 1192341732        }
 1733
 1734        internal static string ToHex(byte[] bytes)
 1295461735        {
 1295461736            if (bytes is null)
 103121737            {
 103121738                return null;
 1739            }
 1740
 1192341741            return ToHex(bytes, 0);
 1295461742        }
 1743
 1744        /// <summary>
 1745        /// Gets a value indicating whether the socket is connected.
 1746        /// </summary>
 1747        /// <returns>
 1748        /// <see langword="true"/> if the socket is connected; otherwise, <see langword="false"/>.
 1749        /// </returns>
 1750        /// <remarks>
 1751        /// <para>
 1752        /// As a first check we verify whether <see cref="Socket.Connected"/> is
 1753        /// <see langword="true"/>. However, this only returns the state of the socket as of
 1754        /// the last I/O operation.
 1755        /// </para>
 1756        /// <para>
 1757        /// Therefore we use the combination of <see cref="Socket.Poll(int, SelectMode)"/> with mode <see cref="SelectMo
 1758        /// and <see cref="Socket.Available"/> to verify if the socket is still connected.
 1759        /// </para>
 1760        /// <para>
 1761        /// The MSDN doc mention the following on the return value of <see cref="Socket.Poll(int, SelectMode)"/>
 1762        /// with mode <see cref="SelectMode.SelectRead"/>:
 1763        /// <list type="bullet">
 1764        ///     <item>
 1765        ///         <description><see langword="true"/> if data is available for reading;</description>
 1766        ///     </item>
 1767        ///     <item>
 1768        ///         <description><see langword="true"/> if the connection has been closed, reset, or terminated; otherwi
 1769        ///     </item>
 1770        /// </list>
 1771        /// </para>
 1772        /// <para>
 1773        /// <c>Conclusion:</c> when the return value is <see langword="true"/> - but no data is available for reading - 
 1774        /// the socket is no longer connected.
 1775        /// </para>
 1776        /// <para>
 1777        /// When a <see cref="Socket"/> is used from multiple threads, there's a race condition
 1778        /// between the invocation of <see cref="Socket.Poll(int, SelectMode)"/> and the moment
 1779        /// when the value of <see cref="Socket.Available"/> is obtained. To workaround this issue
 1780        /// we synchronize reads from the <see cref="Socket"/>.
 1781        /// </para>
 1782        /// </remarks>
 1783        private bool IsSocketConnected()
 43901784        {
 43901785            lock (_socketDisposeLock)
 43901786            {
 43901787                if (!_socket.IsConnected())
 51788                {
 51789                    return false;
 1790                }
 1791
 43851792                lock (_socketReadLock)
 43851793                {
 43851794                    var connectionClosedOrDataAvailable = _socket.Poll(0, SelectMode.SelectRead);
 43851795                    return !(connectionClosedOrDataAvailable && _socket.Available == 0);
 1796                }
 1797            }
 43901798        }
 1799
 1800        /// <summary>
 1801        /// Performs a blocking read on the socket until <paramref name="length"/> bytes are received.
 1802        /// </summary>
 1803        /// <param name="socket">The <see cref="Socket"/> to read from.</param>
 1804        /// <param name="buffer">An array of type <see cref="byte"/> that is the storage location for the received data.
 1805        /// <param name="offset">The position in <paramref name="buffer"/> parameter to store the received data.</param>
 1806        /// <param name="length">The number of bytes to read.</param>
 1807        /// <returns>
 1808        /// The number of bytes read.
 1809        /// </returns>
 1810        /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception>
 1811        /// <exception cref="SocketException">The read failed.</exception>
 1812        private static int TrySocketRead(Socket socket, byte[] buffer, int offset, int length)
 1166831813        {
 1166831814            return SocketAbstraction.Read(socket, buffer, offset, length, InfiniteTimeSpan);
 1166831815        }
 1816
 1817        /// <summary>
 1818        /// Shuts down and disposes the socket.
 1819        /// </summary>
 1820        private void SocketDisconnectAndDispose()
 25471821        {
 25471822            if (_socket != null)
 18731823            {
 18731824                lock (_socketDisposeLock)
 18731825                {
 18731826                    if (_socket != null)
 18171827                    {
 18171828                        if (_socket.Connected)
 18171829                        {
 1830                            try
 18171831                            {
 18171832                                DiagnosticAbstraction.Log(string.Format("[{0}] Shutting down socket.", ToHex(SessionId))
 1833
 1834                                // Interrupt any pending reads; should be done outside of socket read lock as we
 1835                                // actually want shutdown the socket to make sure blocking reads are interrupted.
 1836                                //
 1837                                // This may result in a SocketException (eg. An existing connection was forcibly
 1838                                // closed by the remote host) which we'll log and ignore as it means the socket
 1839                                // was already shut down.
 18171840                                _socket.Shutdown(SocketShutdown.Send);
 18171841                            }
 01842                            catch (SocketException ex)
 01843                            {
 1844                                // TODO: log as warning
 01845                                DiagnosticAbstraction.Log("Failure shutting down socket: " + ex);
 01846                            }
 18171847                        }
 1848
 18171849                        DiagnosticAbstraction.Log(string.Format("[{0}] Disposing socket.", ToHex(SessionId)));
 18171850                        _socket.Dispose();
 18171851                        DiagnosticAbstraction.Log(string.Format("[{0}] Disposed socket.", ToHex(SessionId)));
 18171852                        _socket = null;
 18171853                    }
 18731854                }
 18731855            }
 25471856        }
 1857
 1858        /// <summary>
 1859        /// Listens for incoming message from the server and handles them. This method run as a task on separate thread.
 1860        /// </summary>
 1861        private void MessageListener()
 18171862        {
 1863            try
 18171864            {
 1865                // remain in message loop until socket is shut down or until we're disconnecting
 600691866                while (true)
 600691867                {
 600691868                    var socket = _socket;
 1869
 600691870                    if (socket is null || !socket.Connected)
 6321871                    {
 6321872                        break;
 1873                    }
 1874
 1875                    try
 594371876                    {
 1877                        // Block until either data is available or the socket is closed
 594371878                        var connectionClosedOrDataAvailable = socket.Poll(-1, SelectMode.SelectRead);
 594211879                        if (connectionClosedOrDataAvailable && socket.Available == 0)
 2191880                        {
 1881                            // connection with SSH server was closed or connection was reset
 2191882                            break;
 1883                        }
 583881884                    }
 8141885                    catch (ObjectDisposedException)
 8141886                    {
 1887                        // The socket was disposed by either:
 1888                        // * a call to Disconnect()
 1889                        // * a call to Dispose()
 1890                        // * a SSH_MSG_DISCONNECT received from server
 8141891                        break;
 1892                    }
 1893
 583881894                    var message = ReceiveMessage(socket);
 582981895                    if (message is null)
 451896                    {
 1897                        // Connection with SSH server was closed, so break out of the message loop
 451898                        break;
 1899                    }
 1900
 1901                    // process message
 582531902                    message.Process(this);
 582521903                }
 1904
 1905                // connection with SSH server was closed or socket was disposed
 17101906                RaiseError(CreateConnectionAbortedByServerException());
 17101907            }
 161908            catch (SocketException ex)
 161909            {
 161910                RaiseError(new SshConnectionException(ex.Message, DisconnectReason.ConnectionLost, ex));
 161911            }
 911912            catch (Exception exp)
 911913            {
 911914                RaiseError(exp);
 911915            }
 1916            finally
 18171917            {
 1918                // signal that the message listener thread has stopped
 18171919                _ = _messageListenerCompleted.Set();
 18171920            }
 18171921        }
 1922
 1923        /// <summary>
 1924        /// Raises the <see cref="ErrorOccured"/> event.
 1925        /// </summary>
 1926        /// <param name="exp">The <see cref="Exception"/>.</param>
 1927        private void RaiseError(Exception exp)
 18171928        {
 18171929            var connectionException = exp as SshConnectionException;
 1930
 18171931            DiagnosticAbstraction.Log(string.Format("[{0}] Raised exception: {1}", ToHex(SessionId), exp));
 1932
 18171933            if (_isDisconnecting)
 14771934            {
 1935                // a connection exception which is raised while isDisconnecting is normal and
 1936                // should be ignored
 14771937                if (connectionException != null)
 14771938                {
 14771939                    return;
 1940                }
 1941
 1942                // any timeout while disconnecting can be caused by loss of connectivity
 1943                // altogether and should be ignored
 01944                if (exp is SocketException socketException && socketException.SocketErrorCode == SocketError.TimedOut)
 01945                {
 01946                    return;
 1947                }
 01948            }
 1949
 1950            // "save" exception and set exception wait handle to ensure any waits are interrupted
 3401951            _exception = exp;
 3401952            _ = _exceptionWaitHandle.Set();
 1953
 3401954            ErrorOccured?.Invoke(this, new ExceptionEventArgs(exp));
 1955
 3401956            if (connectionException != null)
 2981957            {
 2981958                DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting after exception: {1}", ToHex(SessionId), ex
 2981959                Disconnect(connectionException.DisconnectReason, exp.ToString());
 2981960            }
 18171961        }
 1962
 1963        /// <summary>
 1964        /// Resets connection-specific information to ensure state of a previous connection
 1965        /// does not affect new connections.
 1966        /// </summary>
 1967        private void Reset()
 18831968        {
 18831969            _ = _exceptionWaitHandle?.Reset();
 18831970            _ = _keyExchangeCompletedWaitHandle?.Reset();
 18831971            _ = _messageListenerCompleted?.Set();
 1972
 18831973            SessionId = null;
 18831974            _isDisconnectMessageSent = false;
 18831975            _isDisconnecting = false;
 18831976            _isAuthenticated = false;
 18831977            _exception = null;
 18831978            _keyExchangeInProgress = false;
 18831979        }
 1980
 1981        private static SshConnectionException CreateConnectionAbortedByServerException()
 17101982        {
 17101983            return new SshConnectionException("An established connection was aborted by the server.",
 17101984                                              DisconnectReason.ConnectionLost);
 17101985        }
 1986
 1987        private bool _disposed;
 1988
 1989        /// <summary>
 1990        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 1991        /// </summary>
 1992        public void Dispose()
 18561993        {
 18561994            Dispose(disposing: true);
 18561995            GC.SuppressFinalize(this);
 18561996        }
 1997
 1998        /// <summary>
 1999        /// Releases unmanaged and - optionally - managed resources.
 2000        /// </summary>
 2001        /// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langwor
 2002        protected virtual void Dispose(bool disposing)
 19702003        {
 19702004            if (_disposed)
 242005            {
 242006                return;
 2007            }
 2008
 19462009            if (disposing)
 18322010            {
 18322011                DiagnosticAbstraction.Log(string.Format("[{0}] Disposing session.", ToHex(SessionId)));
 2012
 18322013                Disconnect();
 2014
 18322015                var serviceAccepted = _serviceAccepted;
 18322016                if (serviceAccepted != null)
 18322017                {
 18322018                    serviceAccepted.Dispose();
 18322019                    _serviceAccepted = null;
 18322020                }
 2021
 18322022                var exceptionWaitHandle = _exceptionWaitHandle;
 18322023                if (exceptionWaitHandle != null)
 18322024                {
 18322025                    exceptionWaitHandle.Dispose();
 18322026                    _exceptionWaitHandle = null;
 18322027                }
 2028
 18322029                var keyExchangeCompletedWaitHandle = _keyExchangeCompletedWaitHandle;
 18322030                if (keyExchangeCompletedWaitHandle != null)
 18322031                {
 18322032                    keyExchangeCompletedWaitHandle.Dispose();
 18322033                    _keyExchangeCompletedWaitHandle = null;
 18322034                }
 2035
 18322036                if (_serverCipher is IDisposable disposableServerCipher)
 11952037                {
 11952038                    disposableServerCipher.Dispose();
 11952039                }
 2040
 18322041                if (_clientCipher is IDisposable disposableClientCipher)
 11952042                {
 11952043                    disposableClientCipher.Dispose();
 11952044                }
 2045
 18322046                var serverMac = _serverMac;
 18322047                if (serverMac != null)
 11982048                {
 11982049                    serverMac.Dispose();
 11982050                    _serverMac = null;
 11982051                }
 2052
 18322053                var clientMac = _clientMac;
 18322054                if (clientMac != null)
 11982055                {
 11982056                    clientMac.Dispose();
 11982057                    _clientMac = null;
 11982058                }
 2059
 18322060                var keyExchange = _keyExchange;
 18322061                if (keyExchange != null)
 12062                {
 12063                    keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
 12064                    keyExchange.Dispose();
 12065                    _keyExchange = null;
 12066                }
 2067
 18322068                var messageListenerCompleted = _messageListenerCompleted;
 18322069                if (messageListenerCompleted != null)
 18322070                {
 18322071                    messageListenerCompleted.Dispose();
 18322072                    _messageListenerCompleted = null;
 18322073                }
 2074
 18322075                _disposed = true;
 18322076            }
 19702077        }
 2078
 2079        /// <summary>
 2080        /// Finalizes an instance of the <see cref="Session"/> class.
 2081        /// </summary>
 2082        ~Session()
 2282083        {
 1142084            Dispose(disposing: false);
 2282085        }
 2086
 2087        /// <summary>
 2088        /// Gets the connection info.
 2089        /// </summary>
 2090        /// <value>The connection info.</value>
 2091        IConnectionInfo ISession.ConnectionInfo
 2092        {
 187742093            get { return ConnectionInfo; }
 2094        }
 2095
 2096        /// <summary>
 2097        /// Gets a <see cref="WaitHandle"/> that can be used to wait for the message listener loop to complete.
 2098        /// </summary>
 2099        /// <value>
 2100        /// A <see cref="WaitHandle"/> that can be used to wait for the message listener loop to complete, or
 2101        /// <see langword="null"/> when the session has not been connected.
 2102        /// </value>
 2103        WaitHandle ISession.MessageListenerCompleted
 2104        {
 2042105            get { return _messageListenerCompleted; }
 2106        }
 2107
 2108        /// <summary>
 2109        /// Create a new SSH session channel.
 2110        /// </summary>
 2111        /// <returns>
 2112        /// A new SSH session channel.
 2113        /// </returns>
 2114        IChannelSession ISession.CreateChannelSession()
 17002115        {
 17002116            return new ChannelSession(this, NextChannelNumber, InitialLocalWindowSize, LocalChannelDataPacketSize);
 17002117        }
 2118
 2119        /// <summary>
 2120        /// Create a new channel for a locally forwarded TCP/IP port.
 2121        /// </summary>
 2122        /// <returns>
 2123        /// A new channel for a locally forwarded TCP/IP port.
 2124        /// </returns>
 2125        IChannelDirectTcpip ISession.CreateChannelDirectTcpip()
 72126        {
 72127            return new ChannelDirectTcpip(this, NextChannelNumber, InitialLocalWindowSize, LocalChannelDataPacketSize);
 72128        }
 2129
 2130        /// <summary>
 2131        /// Creates a "forwarded-tcpip" SSH channel.
 2132        /// </summary>
 2133        /// <param name="remoteChannelNumber">The number of the remote channel.</param>
 2134        /// <param name="remoteWindowSize">The window size of the remote channel.</param>
 2135        /// <param name="remoteChannelDataPacketSize">The data packet size of the remote channel.</param>
 2136        /// <returns>
 2137        /// A new "forwarded-tcpip" SSH channel.
 2138        /// </returns>
 2139        IChannelForwardedTcpip ISession.CreateChannelForwardedTcpip(uint remoteChannelNumber,
 2140                                                                    uint remoteWindowSize,
 2141                                                                    uint remoteChannelDataPacketSize)
 22142        {
 22143            return new ChannelForwardedTcpip(this,
 22144                                             NextChannelNumber,
 22145                                             InitialLocalWindowSize,
 22146                                             LocalChannelDataPacketSize,
 22147                                             remoteChannelNumber,
 22148                                             remoteWindowSize,
 22149                                             remoteChannelDataPacketSize);
 22150        }
 2151
 2152        /// <summary>
 2153        /// Sends a message to the server.
 2154        /// </summary>
 2155        /// <param name="message">The message to send.</param>
 2156        /// <exception cref="SshConnectionException">The client is not connected.</exception>
 2157        /// <exception cref="SshOperationTimeoutException">The operation timed out.</exception>
 2158        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the p
 2159        void ISession.SendMessage(Message message)
 404232160        {
 404232161            SendMessage(message);
 403942162        }
 2163
 2164        /// <summary>
 2165        /// Sends a message to the server.
 2166        /// </summary>
 2167        /// <param name="message">The message to send.</param>
 2168        /// <returns>
 2169        /// <see langword="true"/> if the message was sent to the server; otherwise, <see langword="false"/>.
 2170        /// </returns>
 2171        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the p
 2172        /// <remarks>
 2173        /// This methods returns <see langword="false"/> when the attempt to send the message results in a
 2174        /// <see cref="SocketException"/> or a <see cref="SshException"/>.
 2175        /// </remarks>
 2176        bool ISession.TrySendMessage(Message message)
 27232177        {
 27232178            return TrySendMessage(message);
 27232179        }
 2180    }
 2181
 2182    /// <summary>
 2183    /// Represents the result of a wait operations.
 2184    /// </summary>
 2185    internal enum WaitResult
 2186    {
 2187        /// <summary>
 2188        /// The <see cref="WaitHandle"/> was signaled within the specified interval.
 2189        /// </summary>
 2190        Success = 1,
 2191
 2192        /// <summary>
 2193        /// The <see cref="WaitHandle"/> was not signaled within the specified interval.
 2194        /// </summary>
 2195        TimedOut = 2,
 2196
 2197        /// <summary>
 2198        /// The session is in a disconnected state.
 2199        /// </summary>
 2200        Disconnected = 3,
 2201
 2202        /// <summary>
 2203        /// The session is in a failed state.
 2204        /// </summary>
 2205        Failed = 4
 2206    }
 2207}

Methods/Properties

.cctor()
.ctor(Renci.SshNet.ConnectionInfo,Renci.SshNet.IServiceFactory,Renci.SshNet.Connection.ISocketFactory)
get_SessionSemaphore()
get_NextChannelNumber()
get_IsConnected()
get_SessionId()
get_ClientInitMessage()
get_ServerVersion()
get_ClientVersion()
get_ConnectionInfo()
Connect()
ConnectAsync()
Disconnect()
Disconnect(Renci.SshNet.Messages.Transport.DisconnectReason,System.String)
Renci.SshNet.ISession.WaitOnHandle(System.Threading.WaitHandle)
Renci.SshNet.ISession.WaitOnHandle(System.Threading.WaitHandle,System.TimeSpan)
Renci.SshNet.ISession.TryWait(System.Threading.WaitHandle,System.TimeSpan)
Renci.SshNet.ISession.TryWait(System.Threading.WaitHandle,System.TimeSpan,System.Exception&)
TryWait(System.Threading.WaitHandle,System.TimeSpan,System.Exception&)
WaitOnHandle(System.Threading.WaitHandle)
WaitOnHandle(System.Threading.WaitHandle,System.TimeSpan)
SendMessage(Renci.SshNet.Messages.Message)
SendPacket(System.Byte[],System.Int32,System.Int32)
TrySendMessage(Renci.SshNet.Messages.Message)
ReceiveMessage(System.Net.Sockets.Socket)
TrySendDisconnect(Renci.SshNet.Messages.Transport.DisconnectReason,System.String)
OnDisconnectReceived(Renci.SshNet.Messages.Transport.DisconnectMessage)
OnIgnoreReceived(Renci.SshNet.Messages.Transport.IgnoreMessage)
OnUnimplementedReceived(Renci.SshNet.Messages.Transport.UnimplementedMessage)
OnDebugReceived(Renci.SshNet.Messages.Transport.DebugMessage)
OnServiceRequestReceived(Renci.SshNet.Messages.Transport.ServiceRequestMessage)
OnServiceAcceptReceived(Renci.SshNet.Messages.Transport.ServiceAcceptMessage)
OnKeyExchangeDhGroupExchangeGroupReceived(Renci.SshNet.Messages.Transport.KeyExchangeDhGroupExchangeGroup)
OnKeyExchangeDhGroupExchangeReplyReceived(Renci.SshNet.Messages.Transport.KeyExchangeDhGroupExchangeReply)
OnKeyExchangeInitReceived(Renci.SshNet.Messages.Transport.KeyExchangeInitMessage)
OnKeyExchangeDhReplyMessageReceived(Renci.SshNet.Messages.Transport.KeyExchangeDhReplyMessage)
OnKeyExchangeEcdhReplyMessageReceived(Renci.SshNet.Messages.Transport.KeyExchangeEcdhReplyMessage)
OnNewKeysReceived(Renci.SshNet.Messages.Transport.NewKeysMessage)
Renci.SshNet.ISession.OnDisconnecting()
OnUserAuthenticationRequestReceived(Renci.SshNet.Messages.Authentication.RequestMessage)
OnUserAuthenticationFailureReceived(Renci.SshNet.Messages.Authentication.FailureMessage)
OnUserAuthenticationSuccessReceived(Renci.SshNet.Messages.Authentication.SuccessMessage)
OnUserAuthenticationBannerReceived(Renci.SshNet.Messages.Authentication.BannerMessage)
OnUserAuthenticationInformationRequestReceived(Renci.SshNet.Messages.Authentication.InformationRequestMessage)
OnUserAuthenticationPasswordChangeRequiredReceived(Renci.SshNet.Messages.Authentication.PasswordChangeRequiredMessage)
OnUserAuthenticationPublicKeyReceived(Renci.SshNet.Messages.Authentication.PublicKeyMessage)
OnGlobalRequestReceived(Renci.SshNet.Messages.Connection.GlobalRequestMessage)
OnRequestSuccessReceived(Renci.SshNet.Messages.Connection.RequestSuccessMessage)
OnRequestFailureReceived(Renci.SshNet.Messages.Connection.RequestFailureMessage)
OnChannelOpenReceived(Renci.SshNet.Messages.Connection.ChannelOpenMessage)
OnChannelOpenConfirmationReceived(Renci.SshNet.Messages.Connection.ChannelOpenConfirmationMessage)
OnChannelOpenFailureReceived(Renci.SshNet.Messages.Connection.ChannelOpenFailureMessage)
OnChannelWindowAdjustReceived(Renci.SshNet.Messages.Connection.ChannelWindowAdjustMessage)
OnChannelDataReceived(Renci.SshNet.Messages.Connection.ChannelDataMessage)
OnChannelExtendedDataReceived(Renci.SshNet.Messages.Connection.ChannelExtendedDataMessage)
OnChannelEofReceived(Renci.SshNet.Messages.Connection.ChannelEofMessage)
OnChannelCloseReceived(Renci.SshNet.Messages.Connection.ChannelCloseMessage)
OnChannelRequestReceived(Renci.SshNet.Messages.Connection.ChannelRequestMessage)
OnChannelSuccessReceived(Renci.SshNet.Messages.Connection.ChannelSuccessMessage)
OnChannelFailureReceived(Renci.SshNet.Messages.Connection.ChannelFailureMessage)
KeyExchange_HostKeyReceived(System.Object,Renci.SshNet.Common.HostKeyEventArgs)
RegisterMessage(System.String)
UnRegisterMessage(System.String)
LoadMessage(System.Byte[],System.Int32,System.Int32)
ToHex(System.Byte[],System.Int32)
ToHex(System.Byte[])
IsSocketConnected()
TrySocketRead(System.Net.Sockets.Socket,System.Byte[],System.Int32,System.Int32)
SocketDisconnectAndDispose()
MessageListener()
RaiseError(System.Exception)
Reset()
CreateConnectionAbortedByServerException()
Dispose()
Dispose(System.Boolean)
Finalize()
Renci.SshNet.ISession.get_ConnectionInfo()
Renci.SshNet.ISession.get_MessageListenerCompleted()
Renci.SshNet.ISession.CreateChannelSession()
Renci.SshNet.ISession.CreateChannelDirectTcpip()
Renci.SshNet.ISession.CreateChannelForwardedTcpip(System.UInt32,System.UInt32,System.UInt32)
Renci.SshNet.ISession.SendMessage(Renci.SshNet.Messages.Message)
Renci.SshNet.ISession.TrySendMessage(Renci.SshNet.Messages.Message)