<PackageReference Include="SSH.NET" Version="2016.0.0" />

Session

public class Session : ISession, IDisposable
Provides functionality to connect and interact with SSH server.
using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Compression; using Renci.SshNet.Messages; using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Messages.Connection; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Security; using Renci.SshNet.Security.Cryptography; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; namespace Renci.SshNet { public class Session : ISession, IDisposable { private const byte Null = 0; private const byte CarriageReturn = 13; private const byte LineFeed = 10; internal static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, -1); internal static readonly int Infinite = -1; private const int MaximumSshPacketSize = 68536; private const int InitialLocalWindowSize = 2097152; private const int LocalChannelDataPacketSize = 65536; private static readonly Regex ServerVersionRe = new Regex("^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.+)( SP.+)?$"); private static readonly SemaphoreLight AuthenticationConnection = new SemaphoreLight(3); private SshMessageFactory _sshMessageFactory; private Socket _socket; private readonly object _socketLock = new object(); private EventWaitHandle _messageListenerCompleted; private volatile uint _outboundPacketSequence; private uint _inboundPacketSequence; private EventWaitHandle _serviceAccepted = new AutoResetEvent(false); private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(false); private EventWaitHandle _keyExchangeCompletedWaitHandle = new ManualResetEvent(false); private EventWaitHandle _bytesReadFromSocket = new ManualResetEvent(false); private bool _keyExchangeInProgress; private Exception _exception; private bool _isAuthenticated; private bool _isDisconnecting; private IKeyExchange _keyExchange; private HashAlgorithm _serverMac; private HashAlgorithm _clientMac; private Cipher _clientCipher; private Cipher _serverCipher; private Compressor _serverDecompression; private Compressor _clientCompression; private SemaphoreLight _sessionSemaphore; private readonly IServiceFactory _serviceFactory; private bool _isDisconnectMessageSent; private uint _nextChannelNumber; private Message _clientInitMessage; private bool _disposed; public SemaphoreLight SessionSemaphore { get { if (_sessionSemaphore == null) { lock (this) { if (_sessionSemaphore == null) _sessionSemaphore = new SemaphoreLight(ConnectionInfo.MaxSessions); } } return _sessionSemaphore; } } private uint NextChannelNumber { get { lock (this) { return _nextChannelNumber++; } } } public bool IsConnected { get { if (_disposed || _isDisconnectMessageSent || !_isAuthenticated) return false; if (_messageListenerCompleted == null || _messageListenerCompleted.WaitOne(0)) return false; bool isConnected = false; IsSocketConnected(ref isConnected); return isConnected; } } public byte[] SessionId { get; set; } public Message ClientInitMessage { get { if (_clientInitMessage == null) _clientInitMessage = new KeyExchangeInitMessage { KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(), EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(), MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), LanguagesClientToServer = new string[1] { string.Empty }, LanguagesServerToClient = new string[1] { string.Empty }, FirstKexPacketFollows = false, Reserved = 0 }; return _clientInitMessage; } } public string ServerVersion { get; set; } public string ClientVersion { get; set; } public ConnectionInfo ConnectionInfo { get; set; } IConnectionInfo ISession.ConnectionInfo { get { return ConnectionInfo; } } WaitHandle ISession.MessageListenerCompleted { get { return _messageListenerCompleted; } } public event EventHandler<ExceptionEventArgs> ErrorOccured; public event EventHandler<EventArgs> Disconnected; public event EventHandler<HostKeyEventArgs> HostKeyReceived; public event EventHandler<MessageEventArgs<BannerMessage>> UserAuthenticationBannerReceived; internal event EventHandler<MessageEventArgs<DisconnectMessage>> DisconnectReceived; internal event EventHandler<MessageEventArgs<IgnoreMessage>> IgnoreReceived; internal event EventHandler<MessageEventArgs<UnimplementedMessage>> UnimplementedReceived; internal event EventHandler<MessageEventArgs<DebugMessage>> DebugReceived; internal event EventHandler<MessageEventArgs<ServiceRequestMessage>> ServiceRequestReceived; internal event EventHandler<MessageEventArgs<ServiceAcceptMessage>> ServiceAcceptReceived; internal event EventHandler<MessageEventArgs<KeyExchangeInitMessage>> KeyExchangeInitReceived; internal event EventHandler<MessageEventArgs<NewKeysMessage>> NewKeysReceived; internal event EventHandler<MessageEventArgs<RequestMessage>> UserAuthenticationRequestReceived; internal event EventHandler<MessageEventArgs<FailureMessage>> UserAuthenticationFailureReceived; internal event EventHandler<MessageEventArgs<SuccessMessage>> UserAuthenticationSuccessReceived; internal event EventHandler<MessageEventArgs<GlobalRequestMessage>> GlobalRequestReceived; public event EventHandler<MessageEventArgs<RequestSuccessMessage>> RequestSuccessReceived; public event EventHandler<MessageEventArgs<RequestFailureMessage>> RequestFailureReceived; public event EventHandler<MessageEventArgs<ChannelOpenMessage>> ChannelOpenReceived; public event EventHandler<MessageEventArgs<ChannelOpenConfirmationMessage>> ChannelOpenConfirmationReceived; public event EventHandler<MessageEventArgs<ChannelOpenFailureMessage>> ChannelOpenFailureReceived; public event EventHandler<MessageEventArgs<ChannelWindowAdjustMessage>> ChannelWindowAdjustReceived; public event EventHandler<MessageEventArgs<ChannelDataMessage>> ChannelDataReceived; public event EventHandler<MessageEventArgs<ChannelExtendedDataMessage>> ChannelExtendedDataReceived; public event EventHandler<MessageEventArgs<ChannelEofMessage>> ChannelEofReceived; public event EventHandler<MessageEventArgs<ChannelCloseMessage>> ChannelCloseReceived; public event EventHandler<MessageEventArgs<ChannelRequestMessage>> ChannelRequestReceived; public event EventHandler<MessageEventArgs<ChannelSuccessMessage>> ChannelSuccessReceived; public event EventHandler<MessageEventArgs<ChannelFailureMessage>> ChannelFailureReceived; internal event EventHandler<MessageEventArgs<Message>> MessageReceived; internal Session(ConnectionInfo connectionInfo, IServiceFactory serviceFactory) { if (connectionInfo == null) throw new ArgumentNullException("connectionInfo"); if (serviceFactory == null) throw new ArgumentNullException("serviceFactory"); ClientVersion = "SSH-2.0-Renci.SshNet.SshClient.0.0.1"; ConnectionInfo = connectionInfo; _serviceFactory = serviceFactory; _messageListenerCompleted = new ManualResetEvent(true); } public void Connect() { if (!IsConnected) try { AuthenticationConnection.Wait(); if (!IsConnected) { lock (this) { if (!IsConnected) { Reset(); _sshMessageFactory = new SshMessageFactory(); switch (ConnectionInfo.ProxyType) { case ProxyTypes.None: SocketConnect(ConnectionInfo.Host, ConnectionInfo.Port); break; case ProxyTypes.Socks4: SocketConnect(ConnectionInfo.ProxyHost, ConnectionInfo.ProxyPort); ConnectSocks4(); break; case ProxyTypes.Socks5: SocketConnect(ConnectionInfo.ProxyHost, ConnectionInfo.ProxyPort); ConnectSocks5(); break; case ProxyTypes.Http: SocketConnect(ConnectionInfo.ProxyHost, ConnectionInfo.ProxyPort); ConnectHttp(); break; } string text; Match match; do { text = SocketReadLine(ConnectionInfo.Timeout); if (text == null) throw new SshConnectionException("Server response does not contain SSH protocol identification.", DisconnectReason.ProtocolError); match = ServerVersionRe.Match(text); } while (!match.Success); ServerVersion = text; ConnectionInfo.ServerVersion = ServerVersion; ConnectionInfo.ClientVersion = ClientVersion; string text2 = match.Result("${protoversion}"); match.Result("${softwareversion}"); if (!text2.Equals("2.0") && !text2.Equals("1.99")) throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", new object[1] { text2 }), DisconnectReason.ProtocolVersionNotSupported); SocketAbstraction.Send(_socket, Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\r\n", new object[1] { ClientVersion }))); RegisterMessage("SSH_MSG_DISCONNECT"); RegisterMessage("SSH_MSG_IGNORE"); RegisterMessage("SSH_MSG_UNIMPLEMENTED"); RegisterMessage("SSH_MSG_DEBUG"); RegisterMessage("SSH_MSG_SERVICE_ACCEPT"); RegisterMessage("SSH_MSG_KEXINIT"); RegisterMessage("SSH_MSG_NEWKEYS"); RegisterMessage("SSH_MSG_USERAUTH_BANNER"); _messageListenerCompleted.Reset(); ThreadAbstraction.ExecuteThread(MessageListener); WaitOnHandle(_keyExchangeCompletedWaitHandle); if (SessionId == null) Disconnect(); else { SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication)); WaitOnHandle(_serviceAccepted); if (string.IsNullOrEmpty(ConnectionInfo.Username)) throw new SshException("Username is not specified."); RegisterMessage("SSH_MSG_GLOBAL_REQUEST"); ConnectionInfo.Authenticate(this, _serviceFactory); _isAuthenticated = true; RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); RegisterMessage("SSH_MSG_REQUEST_FAILURE"); RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE"); RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST"); RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA"); RegisterMessage("SSH_MSG_CHANNEL_REQUEST"); RegisterMessage("SSH_MSG_CHANNEL_SUCCESS"); RegisterMessage("SSH_MSG_CHANNEL_FAILURE"); RegisterMessage("SSH_MSG_CHANNEL_DATA"); RegisterMessage("SSH_MSG_CHANNEL_EOF"); RegisterMessage("SSH_MSG_CHANNEL_CLOSE"); Monitor.Pulse(this); } } } } } finally { AuthenticationConnection.Release(); } } public void Disconnect() { Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client."); if (_messageListenerCompleted != null) _messageListenerCompleted.WaitOne(); } private void Disconnect(DisconnectReason reason, string message) { _isDisconnecting = true; if (reason == DisconnectReason.ByApplication) SendDisconnect(reason, message); SocketDisconnectAndDispose(); } void ISession.WaitOnHandle(WaitHandle waitHandle) { WaitOnHandle(waitHandle, ConnectionInfo.Timeout); } internal void WaitOnHandle(WaitHandle waitHandle) { WaitOnHandle(waitHandle, ConnectionInfo.Timeout); } internal void WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout) { if (waitHandle == null) throw new ArgumentNullException("waitHandle"); switch (WaitHandle.WaitAny(new WaitHandle[3] { _exceptionWaitHandle, _messageListenerCompleted, waitHandle }, timeout)) { case 0: throw _exception; case 1: throw new SshConnectionException("Client not connected."); case 258: if (!_isDisconnecting) throw new SshOperationTimeoutException("Session operation has timed out"); break; } } internal void SendMessage(Message message) { if (_socket == null || !_socket.CanWrite()) throw new SshConnectionException("Client not connected."); if (_keyExchangeInProgress && !(message is IKeyExchangedAllowed)) WaitOnHandle(_keyExchangeCompletedWaitHandle); byte paddingMultiplier = (byte)((_clientCipher == null) ? 8 : Math.Max((byte)8, _serverCipher.MinimumSize)); byte[] array = message.GetPacket(paddingMultiplier, _clientCompression); lock (_socketLock) { if (_socket == null || !_socket.Connected) throw new SshConnectionException("Client not connected."); byte[] array2 = null; int num = 4; if (_clientMac != null) { _outboundPacketSequence.Write(array, 0); array2 = _clientMac.ComputeHash(array); } if (_clientCipher != null) { array = _clientCipher.Encrypt(array, num, array.Length - num); num = 0; } if (array.Length > 68536) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Packet is too big. Maximum packet size is {0} bytes.", new object[1] { 68536 })); int num2 = array.Length - num; if (array2 == null) SocketAbstraction.Send(_socket, array, num, num2); else { byte[] array3 = new byte[num2 + _clientMac.HashSize / 8]; Buffer.BlockCopy(array, num, array3, 0, num2); Buffer.BlockCopy(array2, 0, array3, num2, array2.Length); SocketAbstraction.Send(_socket, array3, 0, array3.Length); } _outboundPacketSequence++; Monitor.Pulse(_socketLock); } } private bool TrySendMessage(Message message) { try { SendMessage(message); return true; } catch (SshException) { return false; } catch (SocketException) { return false; } } private Message ReceiveMessage() { byte b = (byte)((_serverCipher == null) ? 8 : Math.Max((byte)8, _serverCipher.MinimumSize)); byte[] array = Read(b); if (_serverCipher != null) array = _serverCipher.Decrypt(array); uint num = (uint)((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]); if (num < Math.Max((byte)16, b) - 4 || num > 68532) throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length: {0}.", new object[1] { num }), DisconnectReason.ProtocolError); int num2 = (int)(num - (b - 4)); byte[] array2 = new byte[num2 + b + 4]; _inboundPacketSequence.Write(array2, 0); Buffer.BlockCopy(array, 0, array2, 4, array.Length); byte[] array3 = null; if (_serverMac != null) { array3 = new byte[_serverMac.HashSize / 8]; num2 += array3.Length; } if (num2 > 0) { byte[] array4 = Read(num2); if (array3 != null) { Buffer.BlockCopy(array4, array4.Length - array3.Length, array3, 0, array3.Length); array4 = array4.Take(array4.Length - array3.Length); } if (array4.Length != 0) { if (_serverCipher != null) array4 = _serverCipher.Decrypt(array4); array4.CopyTo(array2, b + 4); } } byte b2 = array2[8]; int length = (int)(num - b2 - 1); if (_serverMac != null) { byte[] right = _serverMac.ComputeHash(array2); if (!array3.IsEqualTo(right)) throw new SshConnectionException("MAC error", DisconnectReason.MacError); } if (_serverDecompression != null) array2 = _serverDecompression.Decompress(array2, 9, length); _inboundPacketSequence++; return LoadMessage(array2, 9); } private void SendDisconnect(DisconnectReason reasonCode, string message) { if (!_isDisconnectMessageSent && IsConnected) { DisconnectMessage message2 = new DisconnectMessage(reasonCode, message); TrySendMessage(message2); _isDisconnectMessageSent = true; } } private void HandleMessageCore(Message message) { this.HandleMessage((dynamic)message); } private void HandleMessage<T>(T message) where T : Message { OnMessageReceived(message); } private void HandleMessage(DisconnectMessage message) { OnDisconnectReceived(message); Disconnect(message.ReasonCode, message.Description); } private void HandleMessage(IgnoreMessage message) { OnIgnoreReceived(message); } private void HandleMessage(UnimplementedMessage message) { OnUnimplementedReceived(message); } private void HandleMessage(DebugMessage message) { OnDebugReceived(message); } private void HandleMessage(ServiceRequestMessage message) { OnServiceRequestReceived(message); } private void HandleMessage(ServiceAcceptMessage message) { OnServiceAcceptReceived(message); _serviceAccepted.Set(); } private void HandleMessage(KeyExchangeInitMessage message) { OnKeyExchangeInitReceived(message); } private void HandleMessage(NewKeysMessage message) { OnNewKeysReceived(message); } private void HandleMessage(RequestMessage message) { OnUserAuthenticationRequestReceived(message); } private void HandleMessage(FailureMessage message) { OnUserAuthenticationFailureReceived(message); } private void HandleMessage(SuccessMessage message) { OnUserAuthenticationSuccessReceived(message); } private void HandleMessage(BannerMessage message) { OnUserAuthenticationBannerReceived(message); } private void HandleMessage(GlobalRequestMessage message) { OnGlobalRequestReceived(message); } private void HandleMessage(RequestSuccessMessage message) { OnRequestSuccessReceived(message); } private void HandleMessage(RequestFailureMessage message) { OnRequestFailureReceived(message); } private void HandleMessage(ChannelOpenMessage message) { OnChannelOpenReceived(message); } private void HandleMessage(ChannelOpenConfirmationMessage message) { OnChannelOpenConfirmationReceived(message); } private void HandleMessage(ChannelOpenFailureMessage message) { OnChannelOpenFailureReceived(message); } private void HandleMessage(ChannelWindowAdjustMessage message) { OnChannelWindowAdjustReceived(message); } private void HandleMessage(ChannelDataMessage message) { OnChannelDataReceived(message); } private void HandleMessage(ChannelExtendedDataMessage message) { OnChannelExtendedDataReceived(message); } private void HandleMessage(ChannelEofMessage message) { OnChannelEofReceived(message); } private void HandleMessage(ChannelCloseMessage message) { OnChannelCloseReceived(message); } private void HandleMessage(ChannelRequestMessage message) { OnChannelRequestReceived(message); } private void HandleMessage(ChannelSuccessMessage message) { OnChannelSuccessReceived(message); } private void HandleMessage(ChannelFailureMessage message) { OnChannelFailureReceived(message); } protected virtual void OnDisconnectReceived(DisconnectMessage message) { _exception = new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The connection was closed by the server: {0} ({1}).", new object[2] { message.Description, message.ReasonCode }), message.ReasonCode); _exceptionWaitHandle.Set(); this.DisconnectReceived?.Invoke(this, new MessageEventArgs<DisconnectMessage>(message)); this.Disconnected?.Invoke(this, new EventArgs()); } protected virtual void OnIgnoreReceived(IgnoreMessage message) { this.IgnoreReceived?.Invoke(this, new MessageEventArgs<IgnoreMessage>(message)); } protected virtual void OnUnimplementedReceived(UnimplementedMessage message) { this.UnimplementedReceived?.Invoke(this, new MessageEventArgs<UnimplementedMessage>(message)); } protected virtual void OnDebugReceived(DebugMessage message) { this.DebugReceived?.Invoke(this, new MessageEventArgs<DebugMessage>(message)); } protected virtual void OnServiceRequestReceived(ServiceRequestMessage message) { this.ServiceRequestReceived?.Invoke(this, new MessageEventArgs<ServiceRequestMessage>(message)); } protected virtual void OnServiceAcceptReceived(ServiceAcceptMessage message) { this.ServiceAcceptReceived?.Invoke(this, new MessageEventArgs<ServiceAcceptMessage>(message)); } protected virtual void OnKeyExchangeInitReceived(KeyExchangeInitMessage message) { _keyExchangeInProgress = true; _keyExchangeCompletedWaitHandle.Reset(); _sshMessageFactory.DisableNonKeyExchangeMessages(); _keyExchange = _serviceFactory.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, message.KeyExchangeAlgorithms); ConnectionInfo.CurrentKeyExchangeAlgorithm = _keyExchange.Name; _keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived; _keyExchange.Start(this, message); this.KeyExchangeInitReceived?.Invoke(this, new MessageEventArgs<KeyExchangeInitMessage>(message)); } protected virtual void OnNewKeysReceived(NewKeysMessage message) { if (SessionId == null) SessionId = _keyExchange.ExchangeHash; if (_serverMac != null) { Renci.SshNet.Common.Extensions.Dispose(_serverMac); _serverMac = null; } if (_clientMac != null) { Renci.SshNet.Common.Extensions.Dispose(_clientMac); _clientMac = null; } _serverCipher = _keyExchange.CreateServerCipher(); _clientCipher = _keyExchange.CreateClientCipher(); _serverMac = _keyExchange.CreateServerHash(); _clientMac = _keyExchange.CreateClientHash(); _clientCompression = _keyExchange.CreateCompressor(); _serverDecompression = _keyExchange.CreateDecompressor(); if (_keyExchange != null) { _keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; _keyExchange.Dispose(); _keyExchange = null; } _sshMessageFactory.EnableActivatedMessages(); this.NewKeysReceived?.Invoke(this, new MessageEventArgs<NewKeysMessage>(message)); _keyExchangeCompletedWaitHandle.Set(); _keyExchangeInProgress = false; } void ISession.OnDisconnecting() { _isDisconnecting = true; } protected virtual void OnUserAuthenticationRequestReceived(RequestMessage message) { this.UserAuthenticationRequestReceived?.Invoke(this, new MessageEventArgs<RequestMessage>(message)); } protected virtual void OnUserAuthenticationFailureReceived(FailureMessage message) { this.UserAuthenticationFailureReceived?.Invoke(this, new MessageEventArgs<FailureMessage>(message)); } protected virtual void OnUserAuthenticationSuccessReceived(SuccessMessage message) { this.UserAuthenticationSuccessReceived?.Invoke(this, new MessageEventArgs<SuccessMessage>(message)); } protected virtual void OnUserAuthenticationBannerReceived(BannerMessage message) { this.UserAuthenticationBannerReceived?.Invoke(this, new MessageEventArgs<BannerMessage>(message)); } protected virtual void OnGlobalRequestReceived(GlobalRequestMessage message) { this.GlobalRequestReceived?.Invoke(this, new MessageEventArgs<GlobalRequestMessage>(message)); } protected virtual void OnRequestSuccessReceived(RequestSuccessMessage message) { this.RequestSuccessReceived?.Invoke(this, new MessageEventArgs<RequestSuccessMessage>(message)); } protected virtual void OnRequestFailureReceived(RequestFailureMessage message) { this.RequestFailureReceived?.Invoke(this, new MessageEventArgs<RequestFailureMessage>(message)); } protected virtual void OnChannelOpenReceived(ChannelOpenMessage message) { this.ChannelOpenReceived?.Invoke(this, new MessageEventArgs<ChannelOpenMessage>(message)); } protected virtual void OnChannelOpenConfirmationReceived(ChannelOpenConfirmationMessage message) { this.ChannelOpenConfirmationReceived?.Invoke(this, new MessageEventArgs<ChannelOpenConfirmationMessage>(message)); } protected virtual void OnChannelOpenFailureReceived(ChannelOpenFailureMessage message) { this.ChannelOpenFailureReceived?.Invoke(this, new MessageEventArgs<ChannelOpenFailureMessage>(message)); } protected virtual void OnChannelWindowAdjustReceived(ChannelWindowAdjustMessage message) { this.ChannelWindowAdjustReceived?.Invoke(this, new MessageEventArgs<ChannelWindowAdjustMessage>(message)); } protected virtual void OnChannelDataReceived(ChannelDataMessage message) { this.ChannelDataReceived?.Invoke(this, new MessageEventArgs<ChannelDataMessage>(message)); } protected virtual void OnChannelExtendedDataReceived(ChannelExtendedDataMessage message) { this.ChannelExtendedDataReceived?.Invoke(this, new MessageEventArgs<ChannelExtendedDataMessage>(message)); } protected virtual void OnChannelEofReceived(ChannelEofMessage message) { this.ChannelEofReceived?.Invoke(this, new MessageEventArgs<ChannelEofMessage>(message)); } protected virtual void OnChannelCloseReceived(ChannelCloseMessage message) { this.ChannelCloseReceived?.Invoke(this, new MessageEventArgs<ChannelCloseMessage>(message)); } protected virtual void OnChannelRequestReceived(ChannelRequestMessage message) { this.ChannelRequestReceived?.Invoke(this, new MessageEventArgs<ChannelRequestMessage>(message)); } protected virtual void OnChannelSuccessReceived(ChannelSuccessMessage message) { this.ChannelSuccessReceived?.Invoke(this, new MessageEventArgs<ChannelSuccessMessage>(message)); } protected virtual void OnChannelFailureReceived(ChannelFailureMessage message) { this.ChannelFailureReceived?.Invoke(this, new MessageEventArgs<ChannelFailureMessage>(message)); } protected virtual void OnMessageReceived(Message message) { this.MessageReceived?.Invoke(this, new MessageEventArgs<Message>(message)); } private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e) { this.HostKeyReceived?.Invoke(this, e); } private byte[] Read(int length) { byte[] array = new byte[length]; SocketRead(length, array); return array; } public void RegisterMessage(string messageName) { _sshMessageFactory.EnableAndActivateMessage(messageName); } public void UnRegisterMessage(string messageName) { _sshMessageFactory.DisableAndDeactivateMessage(messageName); } private Message LoadMessage(byte[] data, int offset) { byte messageNumber = data[offset]; Message message = _sshMessageFactory.Create(messageNumber); message.Load(data, offset); return message; } private void IsSocketConnected(ref bool isConnected) { isConnected = (_socket != null && _socket.Connected); } private void SocketConnect(string host, int port) { IPEndPoint remoteEndpoint = new IPEndPoint(DnsAbstraction.GetHostAddresses(host)[0], port); _socket = SocketAbstraction.Connect(remoteEndpoint, ConnectionInfo.Timeout); _socket.SendBufferSize = 137072; _socket.ReceiveBufferSize = 137072; } private void SocketRead(int length, byte[] buffer) { if (SocketAbstraction.Read(_socket, buffer, 0, length, InfiniteTimeSpan) > 0) { _bytesReadFromSocket.Set(); return; } if (_isDisconnecting) throw new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost); throw new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost); } private string SocketReadLine(TimeSpan timeout) { Encoding ascii = SshData.Ascii; List<byte> list = new List<byte>(); byte[] array = new byte[1]; while (SocketAbstraction.Read(_socket, array, 0, array.Length, timeout) != 0) { list.Add(array[0]); if (list.Count > 0 && (list[list.Count - 1] == 10 || list[list.Count - 1] == 0)) break; } if (list.Count == 0) return null; if (list.Count == 1 && list[list.Count - 1] == 0) return string.Empty; if (list.Count > 1 && list[list.Count - 2] == 13) return ascii.GetString(list.ToArray(), 0, list.Count - 2); if (list.Count > 1 && list[list.Count - 1] == 10) return ascii.GetString(list.ToArray(), 0, list.Count - 1); return ascii.GetString(list.ToArray(), 0, list.Count); } private void SocketDisconnectAndDispose() { if (_socket != null) { lock (_socketLock) { if (_socket != null) { if (_socket.Connected) { _socket.Shutdown(SocketShutdown.Send); SocketAbstraction.ClearReadBuffer(_socket); } _socket.Dispose(); _socket = null; } } } } private void MessageListener() { try { while (_socket != null && _socket.Connected) { Message message = ReceiveMessage(); HandleMessageCore(message); } } catch (SocketException ex) { RaiseError(new SshConnectionException(ex.Message, DisconnectReason.ConnectionLost, ex)); } catch (Exception exp) { RaiseError(exp); } finally { _messageListenerCompleted.Set(); } } private byte SocketReadByte() { byte[] array = new byte[1]; SocketRead(1, array); return array[0]; } private void SocketWriteByte(byte data) { SocketAbstraction.Send(_socket, new byte[1] { data }); } private void ConnectSocks4() { SocketWriteByte(4); SocketWriteByte(1); SocketWriteByte((byte)(ConnectionInfo.Port / 255)); SocketWriteByte((byte)(ConnectionInfo.Port % 255)); IPAddress iPAddress = DnsAbstraction.GetHostAddresses(ConnectionInfo.Host)[0]; SocketAbstraction.Send(_socket, iPAddress.GetAddressBytes()); byte[] bytes = SshData.Ascii.GetBytes(ConnectionInfo.ProxyUsername); SocketAbstraction.Send(_socket, bytes); SocketWriteByte(0); if (SocketReadByte() != 0) throw new ProxyException("SOCKS4: Null is expected."); switch (SocketReadByte()) { case 91: throw new ProxyException("SOCKS4: Connection rejected."); case 92: throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server."); case 93: throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request."); default: throw new ProxyException("SOCKS4: Not valid response."); case 90: { byte[] buffer = new byte[4]; SocketRead(2, buffer); SocketRead(4, buffer); break; } } } private void ConnectSocks5() { SocketWriteByte(5); SocketWriteByte(2); SocketWriteByte(0); SocketWriteByte(2); byte b = SocketReadByte(); if (b != 5) throw new ProxyException($"""{b}"""); switch (SocketReadByte()) { case 2: { SocketWriteByte(1); byte[] bytes = SshData.Ascii.GetBytes(ConnectionInfo.ProxyUsername); if (bytes.Length > 255) throw new ProxyException("Proxy username is too long."); SocketWriteByte((byte)bytes.Length); SocketAbstraction.Send(_socket, bytes); byte[] bytes2 = SshData.Ascii.GetBytes(ConnectionInfo.ProxyPassword); if (bytes2.Length > 255) throw new ProxyException("Proxy password is too long."); SocketWriteByte((byte)bytes2.Length); SocketAbstraction.Send(_socket, bytes2); if (SocketReadByte() != 1) throw new ProxyException("SOCKS5: Server authentication version is not valid."); if (SocketReadByte() != 0) throw new ProxyException("SOCKS5: Username/Password authentication failed."); break; } case byte.MaxValue: throw new ProxyException("SOCKS5: No acceptable authentication methods were offered."); } SocketWriteByte(5); SocketWriteByte(1); SocketWriteByte(0); IPAddress iPAddress = DnsAbstraction.GetHostAddresses(ConnectionInfo.Host)[0]; if (iPAddress.AddressFamily == AddressFamily.InterNetwork) { SocketWriteByte(1); byte[] addressBytes = iPAddress.GetAddressBytes(); SocketAbstraction.Send(_socket, addressBytes); } else { if (iPAddress.AddressFamily != AddressFamily.InterNetworkV6) throw new ProxyException($"""{iPAddress}"""); SocketWriteByte(4); byte[] addressBytes2 = iPAddress.GetAddressBytes(); SocketAbstraction.Send(_socket, addressBytes2); } SocketWriteByte((byte)(ConnectionInfo.Port / 255)); SocketWriteByte((byte)(ConnectionInfo.Port % 255)); if (SocketReadByte() != 5) throw new ProxyException("SOCKS5: Version 5 is expected."); switch (SocketReadByte()) { case 1: throw new ProxyException("SOCKS5: General failure."); case 2: throw new ProxyException("SOCKS5: Connection not allowed by ruleset."); case 3: throw new ProxyException("SOCKS5: Network unreachable."); case 4: throw new ProxyException("SOCKS5: Host unreachable."); case 5: throw new ProxyException("SOCKS5: Connection refused by destination host."); case 6: throw new ProxyException("SOCKS5: TTL expired."); case 7: throw new ProxyException("SOCKS5: Command not supported or protocol error."); case 8: throw new ProxyException("SOCKS5: Address type not supported."); default: throw new ProxyException("SOCKS4: Not valid response."); case 0: { if (SocketReadByte() != 0) throw new ProxyException("SOCKS5: 0 byte is expected."); byte b2 = SocketReadByte(); byte[] buffer = new byte[16]; switch (b2) { case 1: SocketRead(4, buffer); break; case 4: SocketRead(16, buffer); break; default: throw new ProxyException($"""{b2}"""); } byte[] buffer2 = new byte[2]; SocketRead(2, buffer2); break; } } } private void ConnectHttp() { Regex regex = new Regex("HTTP/(?<version>\\d[.]\\d) (?<statusCode>\\d{3}) (?<reasonPhrase>.+)$"); Regex regex2 = new Regex("(?<fieldName>[^\\[\\]()<>@,;:\\\"/?={} \\t]+):(?<fieldValue>.+)?"); SocketAbstraction.Send(_socket, SshData.Ascii.GetBytes($"""{ConnectionInfo.Host}""{ConnectionInfo.Port}""")); if (!string.IsNullOrEmpty(ConnectionInfo.ProxyUsername)) { string s = $"""{Convert.ToBase64String(SshData.Ascii.GetBytes($"{ConnectionInfo.ProxyUsername}""{ConnectionInfo.ProxyPassword}"))}"""; SocketAbstraction.Send(_socket, SshData.Ascii.GetBytes(s)); } SocketAbstraction.Send(_socket, SshData.Ascii.GetBytes("\r\n")); HttpStatusCode? nullable = null; int num = 0; while (true) { string text = SocketReadLine(ConnectionInfo.Timeout); if (text == null) break; if (!nullable.HasValue) { Match match = regex.Match(text); if (match.Success) { string text2 = match.Result("${statusCode}"); nullable = (HttpStatusCode)int.Parse(text2); if (nullable != HttpStatusCode.OK) { string arg = match.Result("${reasonPhrase}"); throw new ProxyException($"""{text2}""{arg}"""); } } } else { Match match2 = regex2.Match(text); if (match2.Success) { if (match2.Result("${fieldName}").Equals("Content-Length", StringComparison.OrdinalIgnoreCase)) num = int.Parse(match2.Result("${fieldValue}")); } else if (text.Length == 0) { if (num > 0) { byte[] buffer = new byte[num]; SocketRead(num, buffer); } break; } } } if (!nullable.HasValue) throw new ProxyException("HTTP response does not contain status line."); } private void RaiseError(Exception exp) { SshConnectionException ex = exp as SshConnectionException; if (_isDisconnecting) { if (ex != null) return; SocketException ex2 = exp as SocketException; if (ex2 != null && ex2.SocketErrorCode == SocketError.TimedOut) return; } _exception = exp; _exceptionWaitHandle.Set(); this.ErrorOccured?.Invoke(this, new ExceptionEventArgs(exp)); if (ex != null) Disconnect(ex.DisconnectReason, exp.ToString()); } private void Reset() { if (_exceptionWaitHandle != null) _exceptionWaitHandle.Reset(); if (_keyExchangeCompletedWaitHandle != null) _keyExchangeCompletedWaitHandle.Reset(); if (_messageListenerCompleted != null) _messageListenerCompleted.Set(); SessionId = null; _isDisconnectMessageSent = false; _isDisconnecting = false; _isAuthenticated = false; _exception = null; _keyExchangeInProgress = false; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed && disposing) { Disconnect(); EventWaitHandle serviceAccepted = _serviceAccepted; if (serviceAccepted != null) { serviceAccepted.Dispose(); _serviceAccepted = null; } EventWaitHandle exceptionWaitHandle = _exceptionWaitHandle; if (exceptionWaitHandle != null) { exceptionWaitHandle.Dispose(); _exceptionWaitHandle = null; } EventWaitHandle keyExchangeCompletedWaitHandle = _keyExchangeCompletedWaitHandle; if (keyExchangeCompletedWaitHandle != null) { keyExchangeCompletedWaitHandle.Dispose(); _keyExchangeCompletedWaitHandle = null; } HashAlgorithm serverMac = _serverMac; if (serverMac != null) { Renci.SshNet.Common.Extensions.Dispose(serverMac); _serverMac = null; } HashAlgorithm clientMac = _clientMac; if (clientMac != null) { Renci.SshNet.Common.Extensions.Dispose(clientMac); _clientMac = null; } IKeyExchange keyExchange = _keyExchange; if (keyExchange != null) { keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; keyExchange.Dispose(); _keyExchange = null; } EventWaitHandle bytesReadFromSocket = _bytesReadFromSocket; if (bytesReadFromSocket != null) { bytesReadFromSocket.Dispose(); _bytesReadFromSocket = null; } EventWaitHandle messageListenerCompleted = _messageListenerCompleted; if (messageListenerCompleted != null) { messageListenerCompleted.Dispose(); _messageListenerCompleted = null; } _disposed = true; } } ~Session() { Dispose(false); } IChannelSession ISession.CreateChannelSession() { return new ChannelSession(this, NextChannelNumber, 2097152, 65536); } IChannelDirectTcpip ISession.CreateChannelDirectTcpip() { return new ChannelDirectTcpip(this, NextChannelNumber, 2097152, 65536); } IChannelForwardedTcpip ISession.CreateChannelForwardedTcpip(uint remoteChannelNumber, uint remoteWindowSize, uint remoteChannelDataPacketSize) { return new ChannelForwardedTcpip(this, NextChannelNumber, 2097152, 65536, remoteChannelNumber, remoteWindowSize, remoteChannelDataPacketSize); } void ISession.SendMessage(Message message) { SendMessage(message); } bool ISession.TrySendMessage(Message message) { return TrySendMessage(message); } } }