Session
Provides functionality to connect and interact with SSH server.
using Org.BouncyCastle.Utilities;
using Renci.SshNet.Abstractions;
using Renci.SshNet.Channels;
using Renci.SshNet.Common;
using Renci.SshNet.Compression;
using Renci.SshNet.Connection;
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 Renci.SshNet.Security.Cryptography.Ciphers;
using System;
using System.Buffers.Binary;
using System.Globalization;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Renci.SshNet
{
public class Session : ISession, IDisposable
{
internal const byte CarriageReturn = 13;
internal const byte LineFeed = 10;
private static readonly string ClientVersionString = "SSH-2.0-Renci.SshNet.SshClient." + "2024.2.0".Replace('-', '_');
internal const int MaximumSshPacketSize = 68536;
private const int InitialLocalWindowSize = int.MaxValue;
private const int LocalChannelDataPacketSize = 65536;
private readonly IServiceFactory _serviceFactory;
private readonly ISocketFactory _socketFactory;
private readonly object _socketReadLock = new object();
private readonly object _socketWriteLock = new object();
private readonly SemaphoreSlim _socketDisposeLock = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(1, 1);
private SshMessageFactory _sshMessageFactory;
private ManualResetEvent _messageListenerCompleted;
private volatile uint _outboundPacketSequence;
private uint _inboundPacketSequence;
private EventWaitHandle _serviceAccepted = new AutoResetEvent(false);
private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(false);
private ManualResetEventSlim _keyExchangeCompletedWaitHandle = new ManualResetEventSlim(false);
private Exception _exception;
private bool _isAuthenticated;
private bool _isDisconnecting;
private bool _isInitialKex;
private bool _isStrictKex;
private IKeyExchange _keyExchange;
private HashAlgorithm _serverMac;
private HashAlgorithm _clientMac;
private bool _serverEtm;
private bool _clientEtm;
private Cipher _serverCipher;
private Cipher _clientCipher;
private bool _serverAead;
private bool _clientAead;
private Compressor _serverDecompression;
private Compressor _clientCompression;
private SemaphoreSlim _sessionSemaphore;
private bool _isDisconnectMessageSent;
private int _nextChannelNumber;
private Socket _socket;
private bool _disposed;
public SemaphoreSlim SessionSemaphore {
get {
SemaphoreSlim sessionSemaphore = _sessionSemaphore;
if (sessionSemaphore != null)
return sessionSemaphore;
sessionSemaphore = new SemaphoreSlim(ConnectionInfo.MaxSessions);
if (Interlocked.CompareExchange(ref _sessionSemaphore, sessionSemaphore, null) != null)
sessionSemaphore.Dispose();
return _sessionSemaphore;
}
}
private uint NextChannelNumber => (uint)Interlocked.Increment(ref _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; set; }
public string ServerVersion { get; set; }
public string ClientVersion => ClientVersionString;
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<SshIdentificationEventArgs> ServerIdentificationReceived;
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<KeyExchangeEcdhReplyMessage>> KeyExchangeEcdhReplyMessageReceived;
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, ISocketFactory socketFactory)
{
ThrowHelper.ThrowIfNull(connectionInfo, "connectionInfo");
ThrowHelper.ThrowIfNull(serviceFactory, "serviceFactory");
ThrowHelper.ThrowIfNull(socketFactory, "socketFactory");
ConnectionInfo = connectionInfo;
_serviceFactory = serviceFactory;
_socketFactory = socketFactory;
_messageListenerCompleted = new ManualResetEvent(true);
}
public void Connect()
{
if (!IsConnected) {
_connectLock.Wait();
try {
if (!IsConnected) {
Reset();
_sshMessageFactory = new SshMessageFactory();
_socket = _serviceFactory.CreateConnector(ConnectionInfo, _socketFactory).Connect(ConnectionInfo);
SshIdentification sshIdentification = _serviceFactory.CreateProtocolVersionExchange().Start(ClientVersion, _socket, ConnectionInfo.Timeout);
string text3 = ServerVersion = (ConnectionInfo.ServerVersion = sshIdentification.ToString());
ConnectionInfo.ClientVersion = ClientVersion;
if (!sshIdentification.ProtocolVersion.Equals("2.0") && !sshIdentification.ProtocolVersion.Equals("1.99"))
throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", sshIdentification.ProtocolVersion), DisconnectReason.ProtocolVersionNotSupported);
this.ServerIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(sshIdentification));
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");
_isInitialKex = true;
ClientInitMessage = BuildClientInitMessage(true);
SendMessage(ClientInitMessage);
_messageListenerCompleted.Reset();
ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
WaitOnHandle(_keyExchangeCompletedWaitHandle.WaitHandle);
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 {
_connectLock.Release();
}
}
}
[AsyncStateMachine(typeof(<ConnectAsync>d__176))]
public Task ConnectAsync(CancellationToken cancellationToken)
{
<ConnectAsync>d__176 stateMachine = default(<ConnectAsync>d__176);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.cancellationToken = cancellationToken;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
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);
}
WaitResult ISession.TryWait(WaitHandle waitHandle, TimeSpan timeout)
{
Exception exception;
return TryWait(waitHandle, timeout, out exception);
}
WaitResult ISession.TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception)
{
return TryWait(waitHandle, timeout, out exception);
}
private WaitResult TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception)
{
ThrowHelper.ThrowIfNull(waitHandle, "waitHandle");
switch (WaitHandle.WaitAny(new WaitHandle[3] {
_exceptionWaitHandle,
_messageListenerCompleted,
waitHandle
}, timeout)) {
case 0:
if (_exception is SshConnectionException) {
exception = null;
return WaitResult.Disconnected;
}
exception = _exception;
return WaitResult.Failed;
case 1:
exception = null;
return WaitResult.Disconnected;
case 2:
exception = null;
return WaitResult.Success;
case 258:
exception = null;
return WaitResult.TimedOut;
default:
throw new InvalidOperationException("Unexpected result.");
}
}
internal void WaitOnHandle(WaitHandle waitHandle)
{
WaitOnHandle(waitHandle, ConnectionInfo.Timeout);
}
internal void WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
{
ThrowHelper.ThrowIfNull(waitHandle, "waitHandle");
int num = WaitHandle.WaitAny(new WaitHandle[3] {
_exceptionWaitHandle,
_messageListenerCompleted,
waitHandle
}, timeout);
switch (num) {
case 2:
break;
case 0:
ExceptionDispatchInfo.Capture(_exception).Throw();
break;
case 1:
throw new SshConnectionException("Client not connected.");
case 258:
if (!_isDisconnecting)
throw new SshOperationTimeoutException("Session operation has timed out");
break;
default:
throw new SshException("Unexpected element '" + num.ToString(CultureInfo.InvariantCulture) + "' signaled.");
}
}
internal void SendMessage(Message message)
{
if (!_socket.CanWrite())
throw new SshConnectionException("Client not connected.");
if (!_keyExchangeCompletedWaitHandle.IsSet && !(message is IKeyExchangedAllowed))
WaitOnHandle(_keyExchangeCompletedWaitHandle.WaitHandle);
byte paddingMultiplier = (byte)((_clientCipher == null) ? 8 : Math.Max((byte)8, _clientCipher.MinimumSize));
byte[] array = message.GetPacket(paddingMultiplier, _clientCompression, _clientEtm || _clientAead);
lock (_socketWriteLock) {
byte[] array2 = null;
int num = 4;
BinaryPrimitives.WriteUInt32BigEndian(array, _outboundPacketSequence);
if (_clientMac != null && !_clientEtm)
array2 = _clientMac.ComputeHash(array);
if (_clientCipher != null) {
_clientCipher.SetSequenceNumber(_outboundPacketSequence);
if (_clientEtm) {
byte[] array3 = _clientCipher.Encrypt(array, num + 4, array.Length - num - 4);
Array.Resize(ref array, num + 4 + array3.Length);
Buffer.BlockCopy(array3, 0, array, num + 4, array3.Length);
array2 = _clientMac.ComputeHash(array);
} else {
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[] array4 = new byte[num2 + array2.Length];
Buffer.BlockCopy(array, num, array4, 0, num2);
Buffer.BlockCopy(array2, 0, array4, num2, array2.Length);
SendPacket(array4, 0, array4.Length);
}
if (_isStrictKex && message is NewKeysMessage)
_outboundPacketSequence = 0;
else
_outboundPacketSequence++;
}
}
private void SendPacket(byte[] packet, int offset, int length)
{
_socketDisposeLock.Wait();
try {
if (!_socket.IsConnected())
throw new SshConnectionException("Client not connected.");
SocketAbstraction.Send(_socket, packet, offset, length);
} finally {
_socketDisposeLock.Release();
}
}
private bool TrySendMessage(Message message)
{
try {
SendMessage(message);
return true;
} catch (SshException) {
return false;
} catch (SocketException) {
return false;
}
}
private Message ReceiveMessage(Socket socket)
{
int num = (_serverEtm || _serverAead) ? 4 : ((_serverCipher == null) ? 8 : Math.Max((byte)8, _serverCipher.MinimumSize));
int num2 = 0;
if (_serverAead)
num2 = _serverCipher.TagSize;
else if (_serverMac != null) {
num2 = _serverMac.HashSize / 8;
}
uint num3 = default(uint);
byte[] array3 = default(byte[]);
lock (_socketReadLock) {
byte[] array = new byte[num];
if (TrySocketRead(socket, array, 0, num) == 0)
return null;
byte[] array2 = array;
Cipher serverCipher = _serverCipher;
if (serverCipher != null && !(serverCipher is AesGcmCipher)) {
_serverCipher.SetSequenceNumber(_inboundPacketSequence);
if (_serverMac == null || !_serverEtm)
array2 = _serverCipher.Decrypt(array);
}
num3 = BinaryPrimitives.ReadUInt32BigEndian(array2);
if (num3 < Math.Max(8, num) - 4 || num3 > 68532)
throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length: {0}.", num3), DisconnectReason.ProtocolError);
int num4 = (int)(num3 - (num - 4)) + num2;
array3 = new byte[num4 + num + 4];
BinaryPrimitives.WriteUInt32BigEndian(array3, _inboundPacketSequence);
if (_serverAead)
Buffer.BlockCopy(array, 0, array3, 4, num);
else
Buffer.BlockCopy(array2, 0, array3, 4, num);
if (num4 > 0 && TrySocketRead(socket, array3, num + 4, num4) == 0)
return null;
}
if (_serverMac != null && _serverEtm) {
byte[] array4 = _serverMac.ComputeHash(array3, 0, array3.Length - num2);
if (!Arrays.FixedTimeEquals(num2, array4, 0, array3, array3.Length - num2))
throw new SshConnectionException("MAC error", DisconnectReason.MacError);
}
if (_serverCipher != null) {
int num5 = array3.Length - (num + 4 + num2);
if (num5 > 0) {
byte[] array5 = _serverCipher.Decrypt(array3, num + 4, num5);
Buffer.BlockCopy(array5, 0, array3, num + 4, array5.Length);
}
}
byte b = array3[8];
int num6 = (int)(num3 - b - 1);
int offset = 9;
if (_serverMac != null && !_serverEtm) {
byte[] array6 = _serverMac.ComputeHash(array3, 0, array3.Length - num2);
if (!Arrays.FixedTimeEquals(num2, array6, 0, array3, array3.Length - num2))
throw new SshConnectionException("MAC error", DisconnectReason.MacError);
}
if (_serverDecompression != null) {
array3 = _serverDecompression.Decompress(array3, offset, num6);
offset = 0;
num6 = array3.Length;
}
_inboundPacketSequence++;
if (_inboundPacketSequence == uint.MaxValue && _isInitialKex)
throw new SshConnectionException("Inbound packet sequence number is about to wrap during initial key exchange.", DisconnectReason.KeyExchangeFailed);
return LoadMessage(array3, offset, num6);
}
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, EventArgs.Empty);
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)
{
bool isSet = _keyExchangeCompletedWaitHandle.IsSet;
_keyExchangeCompletedWaitHandle.Reset();
if (_isInitialKex && message.KeyExchangeAlgorithms.Contains("kex-strict-s-v00@openssh.com")) {
_isStrictKex = true;
if (_inboundPacketSequence != 1)
throw new SshConnectionException("KEXINIT was not the first packet during strict key exchange.", DisconnectReason.KeyExchangeFailed);
}
_sshMessageFactory.DisableNonKeyExchangeMessages(_isStrictKex);
_keyExchange = _serviceFactory.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, message.KeyExchangeAlgorithms);
ConnectionInfo.CurrentKeyExchangeAlgorithm = _keyExchange.Name;
_keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived;
_keyExchange.Start(this, message, isSet);
this.KeyExchangeInitReceived?.Invoke(this, new MessageEventArgs<KeyExchangeInitMessage>(message));
}
internal void OnKeyExchangeDhReplyMessageReceived(KeyExchangeDhReplyMessage message)
{
this.KeyExchangeDhReplyMessageReceived?.Invoke(this, new MessageEventArgs<KeyExchangeDhReplyMessage>(message));
}
internal void OnKeyExchangeEcdhReplyMessageReceived(KeyExchangeEcdhReplyMessage message)
{
this.KeyExchangeEcdhReplyMessageReceived?.Invoke(this, new MessageEventArgs<KeyExchangeEcdhReplyMessage>(message));
}
internal void OnNewKeysReceived(NewKeysMessage message)
{
if (SessionId == null) {
byte[] array = SessionId = _keyExchange.ExchangeHash;
}
(_serverCipher as IDisposable)?.Dispose();
(_clientCipher as IDisposable)?.Dispose();
if (_serverMac != null) {
_serverMac.Dispose();
_serverMac = null;
}
if (_clientMac != null) {
_clientMac.Dispose();
_clientMac = null;
}
_serverCipher = _keyExchange.CreateServerCipher(out _serverAead);
_clientCipher = _keyExchange.CreateClientCipher(out _clientAead);
_serverMac = _keyExchange.CreateServerHash(out _serverEtm);
_clientMac = _keyExchange.CreateClientHash(out _clientEtm);
_clientCompression = _keyExchange.CreateCompressor();
_serverDecompression = _keyExchange.CreateDecompressor();
_keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
_keyExchange.Dispose();
_keyExchange = null;
_sshMessageFactory.EnableActivatedMessages();
if (_isInitialKex) {
_isInitialKex = false;
ClientInitMessage = BuildClientInitMessage(false);
}
if (_isStrictKex)
_inboundPacketSequence = 0;
this.NewKeysReceived?.Invoke(this, new MessageEventArgs<NewKeysMessage>(message));
_keyExchangeCompletedWaitHandle.Set();
}
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 bool IsSocketConnected()
{
_socketDisposeLock.Wait();
try {
if (_socket.IsConnected()) {
if (Monitor.TryEnter(_socketReadLock))
try {
return !_socket.Poll(0, SelectMode.SelectRead) || _socket.Available != 0;
} finally {
Monitor.Exit(_socketReadLock);
}
return true;
}
return false;
} finally {
_socketDisposeLock.Release();
}
}
private static int TrySocketRead(Socket socket, byte[] buffer, int offset, int length)
{
return SocketAbstraction.Read(socket, buffer, offset, length, Timeout.InfiniteTimeSpan);
}
private void SocketDisconnectAndDispose()
{
if (_socket != null) {
_socketDisposeLock.Wait();
try {
if (_socket != null) {
if (_socket.Connected)
try {
_socket.Shutdown(SocketShutdown.Send);
} catch (SocketException) {
}
_socket.Dispose();
_socket = null;
}
} finally {
_socketDisposeLock.Release();
}
}
}
private void MessageListener()
{
try {
while (true) {
Socket socket = _socket;
if (socket == null || !socket.Connected)
break;
try {
if (socket.Poll(-1, SelectMode.SelectRead) && socket.Available == 0)
break;
} catch (ObjectDisposedException) {
break;
}
Message message = ReceiveMessage(socket);
if (message == null)
break;
message.Process(this);
}
RaiseError(CreateConnectionAbortedByServerException());
} catch (SocketException ex2) {
RaiseError(new SshConnectionException(ex2.Message, DisconnectReason.ConnectionLost, ex2));
} catch (Exception exp) {
RaiseError(exp);
} finally {
_messageListenerCompleted.Set();
}
}
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()
{
_exceptionWaitHandle?.Reset();
_keyExchangeCompletedWaitHandle?.Reset();
_messageListenerCompleted?.Set();
SessionId = null;
_isDisconnectMessageSent = false;
_isDisconnecting = false;
_isAuthenticated = false;
_exception = null;
}
private static SshConnectionException CreateConnectionAbortedByServerException()
{
return new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost);
}
private KeyExchangeInitMessage BuildClientInitMessage(bool includeStrictKexPseudoAlgorithm)
{
KeyExchangeInitMessage keyExchangeInitMessage = new KeyExchangeInitMessage();
keyExchangeInitMessage.KeyExchangeAlgorithms = (includeStrictKexPseudoAlgorithm ? ConnectionInfo.KeyExchangeAlgorithms.Keys.Concat(new <>z__ReadOnlySingleElementList<string>("kex-strict-c-v00@openssh.com")).ToArray() : ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray());
keyExchangeInitMessage.ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray();
keyExchangeInitMessage.EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray();
keyExchangeInitMessage.EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray();
keyExchangeInitMessage.MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray();
keyExchangeInitMessage.MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray();
keyExchangeInitMessage.CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray();
keyExchangeInitMessage.CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray();
keyExchangeInitMessage.LanguagesClientToServer = new string[1] {
string.Empty
};
keyExchangeInitMessage.LanguagesServerToClient = new string[1] {
string.Empty
};
keyExchangeInitMessage.FirstKexPacketFollows = false;
keyExchangeInitMessage.Reserved = 0;
return keyExchangeInitMessage;
}
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;
}
ManualResetEventSlim keyExchangeCompletedWaitHandle = _keyExchangeCompletedWaitHandle;
if (keyExchangeCompletedWaitHandle != null) {
keyExchangeCompletedWaitHandle.Dispose();
_keyExchangeCompletedWaitHandle = null;
}
(_serverCipher as IDisposable)?.Dispose();
(_clientCipher as IDisposable)?.Dispose();
HashAlgorithm serverMac = _serverMac;
if (serverMac != null) {
serverMac.Dispose();
_serverMac = null;
}
HashAlgorithm clientMac = _clientMac;
if (clientMac != null) {
clientMac.Dispose();
_clientMac = null;
}
Compressor serverDecompression = _serverDecompression;
if (serverDecompression != null) {
serverDecompression.Dispose();
_serverDecompression = null;
}
Compressor clientCompression = _clientCompression;
if (clientCompression != null) {
clientCompression.Dispose();
_clientCompression = null;
}
IKeyExchange keyExchange = _keyExchange;
if (keyExchange != null) {
keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
keyExchange.Dispose();
_keyExchange = null;
}
ManualResetEvent messageListenerCompleted = _messageListenerCompleted;
if (messageListenerCompleted != null) {
messageListenerCompleted.Dispose();
_messageListenerCompleted = null;
}
_disposed = true;
}
}
IChannelSession ISession.CreateChannelSession()
{
return new ChannelSession(this, NextChannelNumber, 2147483647, 65536);
}
IChannelDirectTcpip ISession.CreateChannelDirectTcpip()
{
return new ChannelDirectTcpip(this, NextChannelNumber, 2147483647, 65536);
}
IChannelForwardedTcpip ISession.CreateChannelForwardedTcpip(uint remoteChannelNumber, uint remoteWindowSize, uint remoteChannelDataPacketSize)
{
return new ChannelForwardedTcpip(this, NextChannelNumber, 2147483647, 65536, remoteChannelNumber, remoteWindowSize, remoteChannelDataPacketSize);
}
void ISession.SendMessage(Message message)
{
SendMessage(message);
}
bool ISession.TrySendMessage(Message message)
{
return TrySendMessage(message);
}
}
}