<PackageReference Include="SSH.NET" Version="2016.1.0-beta1" />

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 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 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 Socket _socket; private readonly object _socketReadLock = new object(); private readonly object _socketWriteLock = new object(); private readonly object _socketDisposeLock = new object(); 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; return IsSocketConnected(); } } 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<InformationRequestMessage>> UserAuthenticationInformationRequestReceived; internal event EventHandler<MessageEventArgs<PasswordChangeRequiredMessage>> UserAuthenticationPasswordChangeRequiredReceived; internal event EventHandler<MessageEventArgs<PublicKeyMessage>> UserAuthenticationPublicKeyReceived; internal event EventHandler<MessageEventArgs<KeyExchangeDhGroupExchangeGroup>> KeyExchangeDhGroupExchangeGroupReceived; internal event EventHandler<MessageEventArgs<KeyExchangeDhGroupExchangeReply>> KeyExchangeDhGroupExchangeReplyReceived; 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<KeyExchangeDhReplyMessage>> KeyExchangeDhReplyMessageReceived; 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 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.", text2), DisconnectReason.ProtocolVersionNotSupported); SocketAbstraction.Send(_socket, Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\r\n", 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"); } } } } } 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 (IsConnected) TrySendDisconnect(reason, message); SocketDisconnectAndDispose(); } void ISession.WaitOnHandle(WaitHandle waitHandle) { WaitOnHandle(waitHandle, ConnectionInfo.Timeout); } void ISession.WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout) { WaitOnHandle(waitHandle, 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.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 (_socketWriteLock) { 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.", 68536)); int num2 = array.Length - num; if (array2 == null) SendPacket(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); SendPacket(array3, 0, array3.Length); } _outboundPacketSequence++; } } private void SendPacket(byte[] packet, int offset, int length) { lock (_socketDisposeLock) { if (!_socket.IsConnected()) throw new SshConnectionException("Client not connected."); SocketAbstraction.Send(_socket, packet, offset, length); } } 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)); int num = (_serverMac != null) ? (_serverMac.HashSize / 8) : 0; uint num2 = default(uint); byte[] array2 = default(byte[]); lock (_socketReadLock) { byte[] array = new byte[b]; if (TrySocketRead(array, 0, b) == 0) return null; if (_serverCipher != null) array = _serverCipher.Decrypt(array); num2 = (uint)((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]); if (num2 < Math.Max((byte)16, b) - 4 || num2 > 68532) throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length: {0}.", num2), DisconnectReason.ProtocolError); int num3 = (int)(num2 - (b - 4)) + num; array2 = new byte[num3 + b + 4]; _inboundPacketSequence.Write(array2, 0); Buffer.BlockCopy(array, 0, array2, 4, array.Length); if (num3 > 0 && TrySocketRead(array2, b + 4, num3) == 0) return null; } if (_serverCipher != null) { int num4 = array2.Length - (b + 4 + num); if (num4 > 0) { byte[] array3 = _serverCipher.Decrypt(array2, b + 4, num4); Buffer.BlockCopy(array3, 0, array2, b + 4, array3.Length); } } byte b2 = array2[8]; int num5 = (int)(num2 - b2 - 1); int offset = 9; if (_serverMac != null) { byte[] right = _serverMac.ComputeHash(array2, 0, array2.Length - num); if (!array2.Take(array2.Length - num, num).IsEqualTo(right)) throw new SshConnectionException("MAC error", DisconnectReason.MacError); } if (_serverDecompression != null) { array2 = _serverDecompression.Decompress(array2, offset, num5); offset = 0; num5 = array2.Length; } _inboundPacketSequence++; return LoadMessage(array2, offset, num5); } private void TrySendDisconnect(DisconnectReason reasonCode, string message) { DisconnectMessage message2 = new DisconnectMessage(reasonCode, message); TrySendMessage(message2); _isDisconnectMessageSent = true; } internal void OnDisconnectReceived(DisconnectMessage message) { _isDisconnecting = true; _exception = new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The connection was closed by the server: {0} ({1}).", message.Description, message.ReasonCode), message.ReasonCode); _exceptionWaitHandle.Set(); this.DisconnectReceived?.Invoke(this, new MessageEventArgs<DisconnectMessage>(message)); this.Disconnected?.Invoke(this, new EventArgs()); SocketDisconnectAndDispose(); } internal void OnIgnoreReceived(IgnoreMessage message) { this.IgnoreReceived?.Invoke(this, new MessageEventArgs<IgnoreMessage>(message)); } internal void OnUnimplementedReceived(UnimplementedMessage message) { this.UnimplementedReceived?.Invoke(this, new MessageEventArgs<UnimplementedMessage>(message)); } internal void OnDebugReceived(DebugMessage message) { this.DebugReceived?.Invoke(this, new MessageEventArgs<DebugMessage>(message)); } internal void OnServiceRequestReceived(ServiceRequestMessage message) { this.ServiceRequestReceived?.Invoke(this, new MessageEventArgs<ServiceRequestMessage>(message)); } internal void OnServiceAcceptReceived(ServiceAcceptMessage message) { this.ServiceAcceptReceived?.Invoke(this, new MessageEventArgs<ServiceAcceptMessage>(message)); _serviceAccepted.Set(); } internal void OnKeyExchangeDhGroupExchangeGroupReceived(KeyExchangeDhGroupExchangeGroup message) { this.KeyExchangeDhGroupExchangeGroupReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhGroupExchangeGroup>(message)); } internal void OnKeyExchangeDhGroupExchangeReplyReceived(KeyExchangeDhGroupExchangeReply message) { this.KeyExchangeDhGroupExchangeReplyReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhGroupExchangeReply>(message)); } internal 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)); } internal void OnKeyExchangeDhReplyMessageReceived(KeyExchangeDhReplyMessage message) { this.KeyExchangeDhReplyMessageReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhReplyMessage>(message)); } internal void OnNewKeysReceived(NewKeysMessage message) { if (SessionId == null) SessionId = _keyExchange.ExchangeHash; if (_serverMac != null) { _serverMac.Dispose(); _serverMac = null; } if (_clientMac != null) { _clientMac.Dispose(); _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; } internal void OnUserAuthenticationRequestReceived(RequestMessage message) { this.UserAuthenticationRequestReceived?.Invoke(this, new MessageEventArgs<RequestMessage>(message)); } internal void OnUserAuthenticationFailureReceived(FailureMessage message) { this.UserAuthenticationFailureReceived?.Invoke(this, new MessageEventArgs<FailureMessage>(message)); } internal void OnUserAuthenticationSuccessReceived(SuccessMessage message) { this.UserAuthenticationSuccessReceived?.Invoke(this, new MessageEventArgs<SuccessMessage>(message)); } internal void OnUserAuthenticationBannerReceived(BannerMessage message) { this.UserAuthenticationBannerReceived?.Invoke(this, new MessageEventArgs<BannerMessage>(message)); } internal void OnUserAuthenticationInformationRequestReceived(InformationRequestMessage message) { this.UserAuthenticationInformationRequestReceived?.Invoke(this, new MessageEventArgs<InformationRequestMessage>(message)); } internal void OnUserAuthenticationPasswordChangeRequiredReceived(PasswordChangeRequiredMessage message) { this.UserAuthenticationPasswordChangeRequiredReceived?.Invoke(this, new MessageEventArgs<PasswordChangeRequiredMessage>(message)); } internal void OnUserAuthenticationPublicKeyReceived(PublicKeyMessage message) { this.UserAuthenticationPublicKeyReceived?.Invoke(this, new MessageEventArgs<PublicKeyMessage>(message)); } internal void OnGlobalRequestReceived(GlobalRequestMessage message) { this.GlobalRequestReceived?.Invoke(this, new MessageEventArgs<GlobalRequestMessage>(message)); } internal void OnRequestSuccessReceived(RequestSuccessMessage message) { this.RequestSuccessReceived?.Invoke(this, new MessageEventArgs<RequestSuccessMessage>(message)); } internal void OnRequestFailureReceived(RequestFailureMessage message) { this.RequestFailureReceived?.Invoke(this, new MessageEventArgs<RequestFailureMessage>(message)); } internal void OnChannelOpenReceived(ChannelOpenMessage message) { this.ChannelOpenReceived?.Invoke(this, new MessageEventArgs<ChannelOpenMessage>(message)); } internal void OnChannelOpenConfirmationReceived(ChannelOpenConfirmationMessage message) { this.ChannelOpenConfirmationReceived?.Invoke(this, new MessageEventArgs<ChannelOpenConfirmationMessage>(message)); } internal void OnChannelOpenFailureReceived(ChannelOpenFailureMessage message) { this.ChannelOpenFailureReceived?.Invoke(this, new MessageEventArgs<ChannelOpenFailureMessage>(message)); } internal void OnChannelWindowAdjustReceived(ChannelWindowAdjustMessage message) { this.ChannelWindowAdjustReceived?.Invoke(this, new MessageEventArgs<ChannelWindowAdjustMessage>(message)); } internal void OnChannelDataReceived(ChannelDataMessage message) { this.ChannelDataReceived?.Invoke(this, new MessageEventArgs<ChannelDataMessage>(message)); } internal void OnChannelExtendedDataReceived(ChannelExtendedDataMessage message) { this.ChannelExtendedDataReceived?.Invoke(this, new MessageEventArgs<ChannelExtendedDataMessage>(message)); } internal void OnChannelEofReceived(ChannelEofMessage message) { this.ChannelEofReceived?.Invoke(this, new MessageEventArgs<ChannelEofMessage>(message)); } internal void OnChannelCloseReceived(ChannelCloseMessage message) { this.ChannelCloseReceived?.Invoke(this, new MessageEventArgs<ChannelCloseMessage>(message)); } internal void OnChannelRequestReceived(ChannelRequestMessage message) { this.ChannelRequestReceived?.Invoke(this, new MessageEventArgs<ChannelRequestMessage>(message)); } internal void OnChannelSuccessReceived(ChannelSuccessMessage message) { this.ChannelSuccessReceived?.Invoke(this, new MessageEventArgs<ChannelSuccessMessage>(message)); } internal void OnChannelFailureReceived(ChannelFailureMessage message) { this.ChannelFailureReceived?.Invoke(this, new MessageEventArgs<ChannelFailureMessage>(message)); } private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e) { this.HostKeyReceived?.Invoke(this, e); } public void RegisterMessage(string messageName) { _sshMessageFactory.EnableAndActivateMessage(messageName); } public void UnRegisterMessage(string messageName) { _sshMessageFactory.DisableAndDeactivateMessage(messageName); } private Message LoadMessage(byte[] data, int offset, int count) { byte messageNumber = data[offset]; Message message = _sshMessageFactory.Create(messageNumber); message.Load(data, offset + 1, count - 1); return message; } private static string ToHex(byte[] bytes, int offset) { int num = bytes.Length - offset; StringBuilder stringBuilder = new StringBuilder(bytes.Length * 2); for (int i = offset; i < num; i++) { byte b = bytes[i]; stringBuilder.Append(b.ToString("X2")); } return stringBuilder.ToString(); } internal static string ToHex(byte[] bytes) { if (bytes == null) return null; return ToHex(bytes, 0); } 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 int SocketRead(byte[] buffer, int offset, int length) { int num = SocketAbstraction.Read(_socket, buffer, offset, length, InfiniteTimeSpan); if (num == 0) throw new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost); return num; } private bool IsSocketConnected() { lock (_socketDisposeLock) { if (_socket.IsConnected()) { lock (_socketReadLock) { return !_socket.Poll(0, SelectMode.SelectRead) || _socket.Available != 0; } } return false; } } private int TrySocketRead(byte[] buffer, int offset, int length) { return SocketAbstraction.Read(_socket, buffer, offset, length, InfiniteTimeSpan); } 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 (_socketDisposeLock) { if (_socket != null) { if (_socket.Connected) try { _socket.Shutdown(SocketShutdown.Send); } catch (SocketException) { } _socket.Dispose(); _socket = null; } } } } private void MessageListener() { try { List<Socket> checkRead = new List<Socket> { _socket }; while (_socket.IsConnected()) { Socket.Select(checkRead, null, null, -1); if (!_socket.IsConnected()) break; Message message = ReceiveMessage(); if (message == null) break; message.Process(this); } RaiseError(CreateConnectionAbortedByServerException()); } catch (SocketException ex) { RaiseError(new SshConnectionException(((Exception)ex).Message, DisconnectReason.ConnectionLost, (Exception)ex)); } catch (Exception exp) { RaiseError(exp); } finally { _messageListenerCompleted.Set(); } } private byte SocketReadByte() { byte[] array = new byte[1]; SocketRead(array, 0, 1); 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[6]; SocketRead(buffer, 0, 6); 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(buffer, 0, 4); break; case 4: SocketRead(buffer, 0, 16); break; default: throw new ProxyException($"""{b2}"""); } byte[] buffer2 = new byte[2]; SocketRead(buffer2, 0, 2); 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(buffer, 0, num); } 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; } private static SshConnectionException CreateConnectionAbortedByServerException() { return new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost); } 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) { serverMac.Dispose(); _serverMac = null; } HashAlgorithm clientMac = _clientMac; if (clientMac != null) { clientMac.Dispose(); _clientMac = null; } IKeyExchange keyExchange = _keyExchange; if (keyExchange != null) { keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; keyExchange.Dispose(); _keyExchange = 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); } } }