Session
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);
}
}
}