#define TRACE
using System;
using System.Buffers.Binary;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Formats.Asn1;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.CodeAnalysis;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.EdEC;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math.EC.Rfc8032;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Zlib;
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.NetConf;
using Renci.SshNet.Security;
using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
using Renci.SshNet.Security.Cryptography.Ciphers.Paddings;
using Renci.SshNet.Sftp;
using Renci.SshNet.Sftp.Requests;
using Renci.SshNet.Sftp.Responses;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("2024.2.0.1")]
[assembly: AssemblyInformationalVersion("2024.2.0.1+74d4364c32")]
[assembly: CLSCompliant(false)]
[assembly: Guid("ad816c5e-6f13-4589-9f3e-59523f8b77a4")]
[assembly: InternalsVisibleTo("Renci.SshNet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f9194e1eb66b7e2575aaee115ee1d27bc100920e7150e43992d6f668f9737de8b9c7ae892b62b8a36dd1d57929ff1541665d101dc476d6e02390846efae7e5186eec409710fdb596e3f83740afef0d4443055937649bc5a773175b61c57615dac0f0fd10f52b52fedf76c17474cc567b3f7a79de95dde842509fb39aaf69c6c2")]
[assembly: InternalsVisibleTo("Renci.SshNet.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f9194e1eb66b7e2575aaee115ee1d27bc100920e7150e43992d6f668f9737de8b9c7ae892b62b8a36dd1d57929ff1541665d101dc476d6e02390846efae7e5186eec409710fdb596e3f83740afef0d4443055937649bc5a773175b61c57615dac0f0fd10f52b52fedf76c17474cc567b3f7a79de95dde842509fb39aaf69c6c2")]
[assembly: InternalsVisibleTo("Renci.SshNet.Benchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f9194e1eb66b7e2575aaee115ee1d27bc100920e7150e43992d6f668f9737de8b9c7ae892b62b8a36dd1d57929ff1541665d101dc476d6e02390846efae7e5186eec409710fdb596e3f83740afef0d4443055937649bc5a773175b61c57615dac0f0fd10f52b52fedf76c17474cc567b3f7a79de95dde842509fb39aaf69c6c2")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Renci")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Renci 2010-2024")]
[assembly: AssemblyDescription("SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism.")]
[assembly: AssemblyProduct("SSH.NET")]
[assembly: AssemblyTitle("SSH.NET")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/sshnet/SSH.NET.git")]
[assembly: AssemblyVersion("2024.2.0.1")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
[GeneratedCode("Nerdbank.GitVersioning.Tasks", "3.7.77.5761")]
[ExcludeFromCodeCoverage]
internal static class ThisAssembly
{
internal const string AssemblyConfiguration = "Release";
internal const string AssemblyFileVersion = "2024.2.0.1";
internal const string AssemblyInformationalVersion = "2024.2.0.1+74d4364c32";
internal const string AssemblyName = "Renci.SshNet";
internal const string AssemblyTitle = "SSH.NET";
internal const string AssemblyVersion = "2024.2.0.1";
internal static readonly DateTime GitCommitDate = new DateTime(638667472230000000L, DateTimeKind.Utc);
internal const string GitCommitId = "74d4364c32c523f2a7d9a3ddbfc3a304ee9fb023";
internal const bool IsPrerelease = false;
internal const bool IsPublicRelease = true;
internal const string NuGetPackageVersion = "2024.2.0";
internal const string PublicKey = "0024000004800000940000000602000000240000525341310004000001000100f9194e1eb66b7e2575aaee115ee1d27bc100920e7150e43992d6f668f9737de8b9c7ae892b62b8a36dd1d57929ff1541665d101dc476d6e02390846efae7e5186eec409710fdb596e3f83740afef0d4443055937649bc5a773175b61c57615dac0f0fd10f52b52fedf76c17474cc567b3f7a79de95dde842509fb39aaf69c6c2";
internal const string PublicKeyToken = "1cee9f8bde3db106";
internal const string RootNamespace = "Renci.SshNet";
}
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresPreviewFeaturesAttribute : Attribute
{
public string? Message { get; }
public string? Url { get; set; }
public RequiresPreviewFeaturesAttribute()
{
}
public RequiresPreviewFeaturesAttribute(string? message)
{
Message = message;
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CallerArgumentExpressionAttribute : Attribute
{
public string ParameterName { get; }
public CallerArgumentExpressionAttribute(string parameterName)
{
ParameterName = parameterName;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CollectionBuilderAttribute : Attribute
{
public Type BuilderType { get; }
public string MethodName { get; }
public CollectionBuilderAttribute(Type builderType, string methodName)
{
BuilderType = builderType;
MethodName = methodName;
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CompilerFeatureRequiredAttribute : Attribute
{
public const string RefStructs = "RefStructs";
public const string RequiredMembers = "RequiredMembers";
public string FeatureName { get; }
public bool IsOptional { get; set; }
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
{
public string[] Arguments { get; }
public InterpolatedStringHandlerArgumentAttribute(string argument)
{
Arguments = new string[1] { argument };
}
public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
{
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerAttribute : Attribute
{
}
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal static class IsExternalInit
{
}
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ModuleInitializerAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiredMemberAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresLocationAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SkipLocalsInitAttribute : Attribute
{
}
}
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ExperimentalAttribute : Attribute
{
public string DiagnosticId { get; }
public string? UrlFormat { get; set; }
public ExperimentalAttribute(string diagnosticId)
{
DiagnosticId = diagnosticId;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullAttribute : Attribute
{
public string[] Members { get; }
public MemberNotNullAttribute(string member)
{
Members = new string[1] { member };
}
public MemberNotNullAttribute(params string[] members)
{
Members = members;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullWhenAttribute : Attribute
{
public bool ReturnValue { get; }
public string[] Members { get; }
public MemberNotNullWhenAttribute(bool returnValue, string member)
{
ReturnValue = returnValue;
Members = new string[1] { member };
}
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
{
ReturnValue = returnValue;
Members = members;
}
}
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SetsRequiredMembersAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class StringSyntaxAttribute : Attribute
{
public const string CompositeFormat = "CompositeFormat";
public const string DateOnlyFormat = "DateOnlyFormat";
public const string DateTimeFormat = "DateTimeFormat";
public const string EnumFormat = "EnumFormat";
public const string GuidFormat = "GuidFormat";
public const string Json = "Json";
public const string NumericFormat = "NumericFormat";
public const string Regex = "Regex";
public const string TimeOnlyFormat = "TimeOnlyFormat";
public const string TimeSpanFormat = "TimeSpanFormat";
public const string Uri = "Uri";
public const string Xml = "Xml";
public string Syntax { get; }
public object?[] Arguments { get; }
public StringSyntaxAttribute(string syntax)
{
Syntax = syntax;
Arguments = new object[0];
}
public StringSyntaxAttribute(string syntax, params object?[] arguments)
{
Syntax = syntax;
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class UnscopedRefAttribute : Attribute
{
}
}
namespace System.Threading.Tasks
{
internal static class TaskToAsyncResult
{
private sealed class TaskAsyncResult : IAsyncResult
{
internal readonly Task _task;
private readonly AsyncCallback? _callback;
public object? AsyncState { get; }
public bool CompletedSynchronously { get; }
public bool IsCompleted => _task.IsCompleted;
public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle;
internal TaskAsyncResult(Task task, object? state, AsyncCallback? callback)
{
_task = task;
AsyncState = state;
if (task.IsCompleted)
{
CompletedSynchronously = true;
callback?.Invoke(this);
}
else if (callback != null)
{
_callback = callback;
_task.ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().OnCompleted(delegate
{
_callback(this);
});
}
}
}
public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state)
{
if (task == null)
{
throw new ArgumentNullException("task");
}
return new TaskAsyncResult(task, state, callback);
}
public static void End(IAsyncResult asyncResult)
{
Unwrap(asyncResult).GetAwaiter().GetResult();
}
public static TResult End<TResult>(IAsyncResult asyncResult)
{
return Unwrap<TResult>(asyncResult).GetAwaiter().GetResult();
}
public static Task Unwrap(IAsyncResult asyncResult)
{
if (asyncResult == null)
{
throw new ArgumentNullException("asyncResult");
}
return (asyncResult as TaskAsyncResult)?._task ?? throw new ArgumentException(null, "asyncResult");
}
public static Task<TResult> Unwrap<TResult>(IAsyncResult asyncResult)
{
if (asyncResult == null)
{
throw new ArgumentNullException("asyncResult");
}
return ((asyncResult as TaskAsyncResult)?._task as Task<TResult>) ?? throw new ArgumentException(null, "asyncResult");
}
}
}
namespace Renci.SshNet
{
public abstract class AuthenticationMethod : IAuthenticationMethod
{
public abstract string Name { get; }
public string Username { get; private set; }
public string[] AllowedAuthentications { get; protected set; }
protected AuthenticationMethod(string username)
{
ThrowHelper.ThrowIfNullOrWhiteSpace(username, "username");
Username = username;
}
public abstract AuthenticationResult Authenticate(Session session);
AuthenticationResult IAuthenticationMethod.Authenticate(ISession session)
{
return Authenticate((Session)session);
}
}
public enum AuthenticationResult
{
Success,
PartialSuccess,
Failure
}
public abstract class BaseClient : IBaseClient, IDisposable
{
private readonly bool _ownsConnectionInfo;
private readonly IServiceFactory _serviceFactory;
private readonly object _keepAliveLock = new object();
private TimeSpan _keepAliveInterval;
private Timer? _keepAliveTimer;
private ConnectionInfo _connectionInfo;
private bool _isDisposed;
internal ISession? Session { get; private set; }
internal IServiceFactory ServiceFactory => _serviceFactory;
public ConnectionInfo ConnectionInfo
{
get
{
CheckDisposed();
return _connectionInfo;
}
private set
{
_connectionInfo = value;
}
}
public virtual bool IsConnected
{
get
{
CheckDisposed();
return IsSessionConnected();
}
}
public TimeSpan KeepAliveInterval
{
get
{
CheckDisposed();
return _keepAliveInterval;
}
set
{
CheckDisposed();
value.EnsureValidTimeout("KeepAliveInterval");
if (!(value == _keepAliveInterval))
{
if (value == Timeout.InfiniteTimeSpan)
{
StopKeepAliveTimer();
}
else if (_keepAliveTimer != null)
{
_keepAliveTimer.Change(value, value);
}
else if (IsSessionConnected())
{
_keepAliveTimer = CreateKeepAliveTimer(value, value);
}
_keepAliveInterval = value;
}
}
}
public event EventHandler<ExceptionEventArgs>? ErrorOccurred;
public event EventHandler<HostKeyEventArgs>? HostKeyReceived;
public event EventHandler<SshIdentificationEventArgs>? ServerIdentificationReceived;
protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo)
: this(connectionInfo, ownsConnectionInfo, new ServiceFactory())
{
}
private protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory)
{
ThrowHelper.ThrowIfNull(connectionInfo, "connectionInfo");
ThrowHelper.ThrowIfNull(serviceFactory, "serviceFactory");
_connectionInfo = connectionInfo;
_ownsConnectionInfo = ownsConnectionInfo;
_serviceFactory = serviceFactory;
_keepAliveInterval = Timeout.InfiniteTimeSpan;
}
public void Connect()
{
CheckDisposed();
if (IsConnected)
{
throw new InvalidOperationException("The client is already connected.");
}
OnConnecting();
ISession session = Session;
if (session == null || !session.IsConnected)
{
if (session != null)
{
DisposeSession(session);
}
Session = CreateAndConnectSession();
}
try
{
OnConnected();
}
catch
{
DisposeSession();
throw;
}
StartKeepAliveTimer();
}
public async Task ConnectAsync(CancellationToken cancellationToken)
{
CheckDisposed();
cancellationToken.ThrowIfCancellationRequested();
if (IsConnected)
{
throw new InvalidOperationException("The client is already connected.");
}
OnConnecting();
ISession session = Session;
if (session == null || !session.IsConnected)
{
if (session != null)
{
DisposeSession(session);
}
using CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource(ConnectionInfo.Timeout);
using CancellationTokenSource linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellationTokenSource.Token);
try
{
Session = await CreateAndConnectSessionAsync(linkedCancellationTokenSource.Token).ConfigureAwait(continueOnCapturedContext: false);
}
catch (OperationCanceledException innerException) when (timeoutCancellationTokenSource.IsCancellationRequested)
{
throw new SshOperationTimeoutException("Connection has timed out.", innerException);
}
}
try
{
OnConnected();
}
catch
{
DisposeSession();
throw;
}
StartKeepAliveTimer();
}
public void Disconnect()
{
CheckDisposed();
OnDisconnecting();
StopKeepAliveTimer();
DisposeSession();
OnDisconnected();
}
[Obsolete("Use KeepAliveInterval to send a keep-alive message at regular intervals.")]
public void SendKeepAlive()
{
CheckDisposed();
SendKeepAliveMessage();
}
protected virtual void OnConnecting()
{
}
protected virtual void OnConnected()
{
}
protected virtual void OnDisconnecting()
{
Session?.OnDisconnecting();
}
protected virtual void OnDisconnected()
{
}
private void Session_ErrorOccured(object? sender, ExceptionEventArgs e)
{
this.ErrorOccurred?.Invoke(this, e);
}
private void Session_HostKeyReceived(object? sender, HostKeyEventArgs e)
{
this.HostKeyReceived?.Invoke(this, e);
}
private void Session_ServerIdentificationReceived(object? sender, SshIdentificationEventArgs e)
{
this.ServerIdentificationReceived?.Invoke(this, e);
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed && disposing)
{
Disconnect();
if (_ownsConnectionInfo && _connectionInfo is IDisposable disposable)
{
disposable.Dispose();
}
_isDisposed = true;
}
}
protected void CheckDisposed()
{
ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
}
private void StopKeepAliveTimer()
{
if (_keepAliveTimer != null)
{
_keepAliveTimer.Dispose();
_keepAliveTimer = null;
}
}
private void SendKeepAliveMessage()
{
ISession session = Session;
if (session == null || !Monitor.TryEnter(_keepAliveLock))
{
return;
}
try
{
session.TrySendMessage(new IgnoreMessage());
}
finally
{
Monitor.Exit(_keepAliveLock);
}
}
private void StartKeepAliveTimer()
{
if (!(_keepAliveInterval == Timeout.InfiniteTimeSpan) && _keepAliveTimer == null)
{
_keepAliveTimer = CreateKeepAliveTimer(_keepAliveInterval, _keepAliveInterval);
}
}
private Timer CreateKeepAliveTimer(TimeSpan dueTime, TimeSpan period)
{
return new Timer(delegate
{
SendKeepAliveMessage();
}, Session, dueTime, period);
}
private ISession CreateAndConnectSession()
{
ISession session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory());
session.ServerIdentificationReceived += Session_ServerIdentificationReceived;
session.HostKeyReceived += Session_HostKeyReceived;
session.ErrorOccured += Session_ErrorOccured;
try
{
session.Connect();
return session;
}
catch
{
DisposeSession(session);
throw;
}
}
private async Task<ISession> CreateAndConnectSessionAsync(CancellationToken cancellationToken)
{
ISession session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory());
session.ServerIdentificationReceived += Session_ServerIdentificationReceived;
session.HostKeyReceived += Session_HostKeyReceived;
session.ErrorOccured += Session_ErrorOccured;
try
{
await session.ConnectAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
return session;
}
catch
{
DisposeSession(session);
throw;
}
}
private void DisposeSession(ISession session)
{
session.ErrorOccured -= Session_ErrorOccured;
session.HostKeyReceived -= Session_HostKeyReceived;
session.ServerIdentificationReceived -= Session_ServerIdentificationReceived;
session.Dispose();
}
private void DisposeSession()
{
ISession session = Session;
if (session != null)
{
Session = null;
DisposeSession(session);
}
}
private bool IsSessionConnected()
{
return Session?.IsConnected ?? false;
}
}
public class CipherInfo
{
public int KeySize { get; private set; }
public bool IsAead { get; private set; }
public Func<byte[], byte[], Cipher> Cipher { get; private set; }
public CipherInfo(int keySize, Func<byte[], byte[], Cipher> cipher, bool isAead = false)
{
CipherInfo cipherInfo = this;
KeySize = keySize;
Cipher = (byte[] key, byte[] iv) => cipher(key.Take(cipherInfo.KeySize / 8), iv);
IsAead = isAead;
}
}
internal sealed class ClientAuthentication : IClientAuthentication
{
private sealed class AuthenticationState
{
private readonly IList<IAuthenticationMethod> _supportedAuthenticationMethods;
private readonly Dictionary<IAuthenticationMethod, int> _authenticationMethodPartialSuccessRegister;
private readonly List<IAuthenticationMethod> _failedAuthenticationMethods;
public AuthenticationState(IList<IAuthenticationMethod> supportedAuthenticationMethods)
{
_supportedAuthenticationMethods = supportedAuthenticationMethods;
_failedAuthenticationMethods = new List<IAuthenticationMethod>();
_authenticationMethodPartialSuccessRegister = new Dictionary<IAuthenticationMethod, int>();
}
public void RecordFailure(IAuthenticationMethod authenticationMethod)
{
_failedAuthenticationMethods.Add(authenticationMethod);
}
public void RecordPartialSuccess(IAuthenticationMethod authenticationMethod)
{
if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out var value))
{
_authenticationMethodPartialSuccessRegister[authenticationMethod] = value + 1;
}
else
{
_authenticationMethodPartialSuccessRegister.Add(authenticationMethod, 1);
}
}
public int GetPartialSuccessCount(IAuthenticationMethod authenticationMethod)
{
if (_authenticationMethodPartialSuccessRegister.TryGetValue(authenticationMethod, out var value))
{
return value;
}
return 0;
}
public List<IAuthenticationMethod> GetSupportedAuthenticationMethods(string[] allowedAuthenticationMethods)
{
List<IAuthenticationMethod> list = new List<IAuthenticationMethod>();
foreach (IAuthenticationMethod supportedAuthenticationMethod in _supportedAuthenticationMethods)
{
string name = supportedAuthenticationMethod.Name;
for (int i = 0; i < allowedAuthenticationMethods.Length; i++)
{
if (allowedAuthenticationMethods[i] == name)
{
list.Add(supportedAuthenticationMethod);
break;
}
}
}
return list;
}
public IEnumerable<IAuthenticationMethod> GetActiveAuthenticationMethods(List<IAuthenticationMethod> matchingAuthenticationMethods)
{
List<IAuthenticationMethod> skippedAuthenticationMethods = new List<IAuthenticationMethod>();
for (int i = 0; i < matchingAuthenticationMethods.Count; i++)
{
IAuthenticationMethod authenticationMethod = matchingAuthenticationMethods[i];
if (!_failedAuthenticationMethods.Contains(authenticationMethod))
{
if (_authenticationMethodPartialSuccessRegister.ContainsKey(authenticationMethod))
{
skippedAuthenticationMethods.Add(authenticationMethod);
}
else
{
yield return authenticationMethod;
}
}
}
foreach (IAuthenticationMethod item in skippedAuthenticationMethods)
{
yield return item;
}
}
}
private readonly int _partialSuccessLimit;
internal int PartialSuccessLimit => _partialSuccessLimit;
public ClientAuthentication(int partialSuccessLimit)
{
if (partialSuccessLimit < 1)
{
throw new ArgumentOutOfRangeException("partialSuccessLimit", "Cannot be less than one.");
}
_partialSuccessLimit = partialSuccessLimit;
}
public void Authenticate(IConnectionInfoInternal connectionInfo, ISession session)
{
ThrowHelper.ThrowIfNull(connectionInfo, "connectionInfo");
ThrowHelper.ThrowIfNull(session, "session");
session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE");
session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
session.RegisterMessage("SSH_MSG_USERAUTH_BANNER");
session.UserAuthenticationBannerReceived += connectionInfo.UserAuthenticationBannerReceived;
try
{
SshAuthenticationException authenticationException = null;
IAuthenticationMethod authenticationMethod = connectionInfo.CreateNoneAuthenticationMethod();
if (authenticationMethod.Authenticate(session) != 0 && !TryAuthenticate(session, new AuthenticationState(connectionInfo.AuthenticationMethods), authenticationMethod.AllowedAuthentications, ref authenticationException))
{
throw authenticationException;
}
}
finally
{
session.UserAuthenticationBannerReceived -= connectionInfo.UserAuthenticationBannerReceived;
session.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE");
session.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS");
session.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER");
}
}
private bool TryAuthenticate(ISession session, AuthenticationState authenticationState, string[] allowedAuthenticationMethods, ref SshAuthenticationException authenticationException)
{
if (allowedAuthenticationMethods.Length == 0)
{
authenticationException = new SshAuthenticationException("No authentication methods defined on SSH server.");
return false;
}
List<IAuthenticationMethod> supportedAuthenticationMethods = authenticationState.GetSupportedAuthenticationMethods(allowedAuthenticationMethods);
if (supportedAuthenticationMethods.Count == 0)
{
authenticationException = new SshAuthenticationException(string.Format(CultureInfo.InvariantCulture, "No suitable authentication method found to complete authentication ({0}).", string.Join(',', allowedAuthenticationMethods)));
return false;
}
foreach (IAuthenticationMethod activeAuthenticationMethod in authenticationState.GetActiveAuthenticationMethods(supportedAuthenticationMethods))
{
if (authenticationState.GetPartialSuccessCount(activeAuthenticationMethod) >= _partialSuccessLimit)
{
authenticationException = new SshAuthenticationException($"Reached authentication attempt limit for method ({activeAuthenticationMethod.Name}).");
continue;
}
AuthenticationResult authenticationResult = activeAuthenticationMethod.Authenticate(session);
switch (authenticationResult)
{
case AuthenticationResult.PartialSuccess:
authenticationState.RecordPartialSuccess(activeAuthenticationMethod);
if (TryAuthenticate(session, authenticationState, activeAuthenticationMethod.AllowedAuthentications, ref authenticationException))
{
authenticationResult = AuthenticationResult.Success;
}
break;
case AuthenticationResult.Failure:
authenticationState.RecordFailure(activeAuthenticationMethod);
authenticationException = new SshAuthenticationException($"Permission denied ({activeAuthenticationMethod.Name}).");
break;
case AuthenticationResult.Success:
authenticationException = null;
break;
}
if (authenticationResult != 0)
{
continue;
}
return true;
}
return false;
}
}
public class ConnectionInfo : IConnectionInfoInternal, IConnectionInfo
{
internal const int DefaultPort = 22;
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30.0);
private static readonly TimeSpan DefaultChannelCloseTimeout = TimeSpan.FromSeconds(1.0);
private TimeSpan _timeout;
private TimeSpan _channelCloseTimeout;
public IDictionary<string, Func<IKeyExchange>> KeyExchangeAlgorithms { get; private set; }
public IDictionary<string, CipherInfo> Encryptions { get; private set; }
public IDictionary<string, HashInfo> HmacAlgorithms { get; private set; }
public IDictionary<string, Func<byte[], KeyHostAlgorithm>> HostKeyAlgorithms { get; private set; }
public IList<AuthenticationMethod> AuthenticationMethods { get; private set; }
public IDictionary<string, Func<Compressor>> CompressionAlgorithms { get; private set; }
public IDictionary<string, RequestInfo> ChannelRequests { get; private set; }
public bool IsAuthenticated { get; private set; }
public string Host { get; private set; }
public int Port { get; private set; }
public string Username { get; private set; }
public ProxyTypes ProxyType { get; private set; }
public string ProxyHost { get; private set; }
public int ProxyPort { get; private set; }
public string ProxyUsername { get; private set; }
public string ProxyPassword { get; private set; }
public TimeSpan Timeout
{
get
{
return _timeout;
}
set
{
value.EnsureValidTimeout("Timeout");
_timeout = value;
}
}
public TimeSpan ChannelCloseTimeout
{
get
{
return _channelCloseTimeout;
}
set
{
value.EnsureValidTimeout("ChannelCloseTimeout");
_channelCloseTimeout = value;
}
}
public Encoding Encoding { get; set; }
public int RetryAttempts { get; set; }
public int MaxSessions { get; set; }
public string CurrentKeyExchangeAlgorithm { get; internal set; }
public string CurrentServerEncryption { get; internal set; }
public string CurrentClientEncryption { get; internal set; }
public string CurrentServerHmacAlgorithm { get; internal set; }
public string CurrentClientHmacAlgorithm { get; internal set; }
public string CurrentHostKeyAlgorithm { get; internal set; }
public string CurrentServerCompressionAlgorithm { get; internal set; }
public string ServerVersion { get; internal set; }
public string ClientVersion { get; internal set; }
public string CurrentClientCompressionAlgorithm { get; internal set; }
IList<IAuthenticationMethod> IConnectionInfoInternal.AuthenticationMethods => AuthenticationMethods.Cast<IAuthenticationMethod>().ToList();
public event EventHandler<AuthenticationBannerEventArgs> AuthenticationBanner;
public ConnectionInfo(string host, string username, params AuthenticationMethod[] authenticationMethods)
: this(host, 22, username, ProxyTypes.None, null, 0, null, null, authenticationMethods)
{
}
public ConnectionInfo(string host, int port, string username, params AuthenticationMethod[] authenticationMethods)
: this(host, port, username, ProxyTypes.None, null, 0, null, null, authenticationMethods)
{
}
public ConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params AuthenticationMethod[] authenticationMethods)
{
ThrowHelper.ThrowIfNull(host, "host");
port.ValidatePort("port");
ThrowHelper.ThrowIfNullOrWhiteSpace(username, "username");
if (proxyType != 0)
{
ThrowHelper.ThrowIfNull(proxyHost, "proxyHost");
proxyPort.ValidatePort("proxyPort");
}
ThrowHelper.ThrowIfNull(authenticationMethods, "authenticationMethods");
if (authenticationMethods.Length == 0)
{
throw new ArgumentException("At least one authentication method should be specified.", "authenticationMethods");
}
Timeout = DefaultTimeout;
ChannelCloseTimeout = DefaultChannelCloseTimeout;
RetryAttempts = 10;
MaxSessions = 10;
Encoding = Encoding.UTF8;
KeyExchangeAlgorithms = new Dictionary<string, Func<IKeyExchange>>
{
{
"curve25519-sha256",
() => new KeyExchangeECCurve25519()
},
{
"[email protected]",
() => new KeyExchangeECCurve25519()
},
{
"ecdh-sha2-nistp256",
() => new KeyExchangeECDH256()
},
{
"ecdh-sha2-nistp384",
() => new KeyExchangeECDH384()
},
{
"ecdh-sha2-nistp521",
() => new KeyExchangeECDH521()
},
{
"diffie-hellman-group-exchange-sha256",
() => new KeyExchangeDiffieHellmanGroupExchangeSha256()
},
{
"diffie-hellman-group-exchange-sha1",
() => new KeyExchangeDiffieHellmanGroupExchangeSha1()
},
{
"diffie-hellman-group16-sha512",
() => new KeyExchangeDiffieHellmanGroup16Sha512()
},
{
"diffie-hellman-group14-sha256",
() => new KeyExchangeDiffieHellmanGroup14Sha256()
},
{
"diffie-hellman-group14-sha1",
() => new KeyExchangeDiffieHellmanGroup14Sha1()
},
{
"diffie-hellman-group1-sha1",
() => new KeyExchangeDiffieHellmanGroup1Sha1()
}
};
Encryptions = new Dictionary<string, CipherInfo>
{
{
"aes128-ctr",
new CipherInfo(128, (byte[] key, byte[] iv) => new AesCipher(key, iv, AesCipherMode.CTR))
},
{
"aes192-ctr",
new CipherInfo(192, (byte[] key, byte[] iv) => new AesCipher(key, iv, AesCipherMode.CTR))
},
{
"aes256-ctr",
new CipherInfo(256, (byte[] key, byte[] iv) => new AesCipher(key, iv, AesCipherMode.CTR))
},
{
"[email protected]",
new CipherInfo(128, (byte[] key, byte[] iv) => new AesGcmCipher(key, iv, 4), isAead: true)
},
{
"[email protected]",
new CipherInfo(256, (byte[] key, byte[] iv) => new AesGcmCipher(key, iv, 4), isAead: true)
},
{
"[email protected]",
new CipherInfo(512, (byte[] key, byte[] iv) => new ChaCha20Poly1305Cipher(key, 4), isAead: true)
},
{
"aes128-cbc",
new CipherInfo(128, (byte[] key, byte[] iv) => new AesCipher(key, iv, AesCipherMode.CBC))
},
{
"aes192-cbc",
new CipherInfo(192, (byte[] key, byte[] iv) => new AesCipher(key, iv, AesCipherMode.CBC))
},
{
"aes256-cbc",
new CipherInfo(256, (byte[] key, byte[] iv) => new AesCipher(key, iv, AesCipherMode.CBC))
},
{
"3des-cbc",
new CipherInfo(192, (byte[] key, byte[] iv) => new TripleDesCipher(key, new CbcCipherMode(iv), null))
}
};
HmacAlgorithms = new Dictionary<string, HashInfo>
{
{
"hmac-sha2-256",
new HashInfo(256, (byte[] key) => new HMACSHA256(key))
},
{
"hmac-sha2-512",
new HashInfo(512, (byte[] key) => new HMACSHA512(key))
},
{
"hmac-sha1",
new HashInfo(160, (byte[] key) => new HMACSHA1(key))
},
{
"[email protected]",
new HashInfo(256, (byte[] key) => new HMACSHA256(key), isEncryptThenMAC: true)
},
{
"[email protected]",
new HashInfo(512, (byte[] key) => new HMACSHA512(key), isEncryptThenMAC: true)
},
{
"[email protected]",
new HashInfo(160, (byte[] key) => new HMACSHA1(key), isEncryptThenMAC: true)
}
};
Dictionary<string, Func<byte[], KeyHostAlgorithm>> hostAlgs = new Dictionary<string, Func<byte[], KeyHostAlgorithm>>();
hostAlgs.Add("[email protected]", delegate(byte[] data)
{
Certificate certificate8 = new Certificate(data);
return new CertificateHostAlgorithm("[email protected]", certificate8, hostAlgs);
});
hostAlgs.Add("[email protected]", delegate(byte[] data)
{
Certificate certificate7 = new Certificate(data);
return new CertificateHostAlgorithm("[email protected]", certificate7, hostAlgs);
});
hostAlgs.Add("[email protected]", delegate(byte[] data)
{
Certificate certificate6 = new Certificate(data);
return new CertificateHostAlgorithm("[email protected]", certificate6, hostAlgs);
});
hostAlgs.Add("[email protected]", delegate(byte[] data)
{
Certificate certificate5 = new Certificate(data);
return new CertificateHostAlgorithm("[email protected]", certificate5, hostAlgs);
});
hostAlgs.Add("[email protected]", delegate(byte[] data)
{
Certificate certificate4 = new Certificate(data);
return new CertificateHostAlgorithm("[email protected]", certificate4, new RsaDigitalSignature((RsaKey)certificate4.Key, HashAlgorithmName.SHA512), hostAlgs);
});
hostAlgs.Add("[email protected]", delegate(byte[] data)
{
Certificate certificate3 = new Certificate(data);
return new CertificateHostAlgorithm("[email protected]", certificate3, new RsaDigitalSignature((RsaKey)certificate3.Key, HashAlgorithmName.SHA256), hostAlgs);
});
hostAlgs.Add("[email protected]", delegate(byte[] data)
{
Certificate certificate2 = new Certificate(data);
return new CertificateHostAlgorithm("[email protected]", certificate2, hostAlgs);
});
hostAlgs.Add("[email protected]", delegate(byte[] data)
{
Certificate certificate = new Certificate(data);
return new CertificateHostAlgorithm("[email protected]", certificate, hostAlgs);
});
hostAlgs.Add("ssh-ed25519", (byte[] data) => new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(new SshKeyData(data))));
hostAlgs.Add("ecdsa-sha2-nistp256", (byte[] data) => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(new SshKeyData(data))));
hostAlgs.Add("ecdsa-sha2-nistp384", (byte[] data) => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(new SshKeyData(data))));
hostAlgs.Add("ecdsa-sha2-nistp521", (byte[] data) => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(new SshKeyData(data))));
hostAlgs.Add("rsa-sha2-512", delegate(byte[] data)
{
RsaKey rsaKey2 = new RsaKey(new SshKeyData(data));
return new KeyHostAlgorithm("rsa-sha2-512", rsaKey2, new RsaDigitalSignature(rsaKey2, HashAlgorithmName.SHA512));
});
hostAlgs.Add("rsa-sha2-256", delegate(byte[] data)
{
RsaKey rsaKey = new RsaKey(new SshKeyData(data));
return new KeyHostAlgorithm("rsa-sha2-256", rsaKey, new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256));
});
hostAlgs.Add("ssh-rsa", (byte[] data) => new KeyHostAlgorithm("ssh-rsa", new RsaKey(new SshKeyData(data))));
hostAlgs.Add("ssh-dss", (byte[] data) => new KeyHostAlgorithm("ssh-dss", new DsaKey(new SshKeyData(data))));
HostKeyAlgorithms = hostAlgs;
CompressionAlgorithms = new Dictionary<string, Func<Compressor>>
{
{ "none", null },
{
"[email protected]",
() => new ZlibOpenSsh()
}
};
ChannelRequests = new Dictionary<string, RequestInfo>
{
{
"env",
new EnvironmentVariableRequestInfo()
},
{
"exec",
new ExecRequestInfo()
},
{
"exit-signal",
new ExitSignalRequestInfo()
},
{
"exit-status",
new ExitStatusRequestInfo()
},
{
"pty-req",
new PseudoTerminalRequestInfo()
},
{
"shell",
new ShellRequestInfo()
},
{
"signal",
new SignalRequestInfo()
},
{
"subsystem",
new SubsystemRequestInfo()
},
{
"window-change",
new WindowChangeRequestInfo()
},
{
"x11-req",
new X11ForwardingRequestInfo()
},
{
"xon-xoff",
new XonXoffRequestInfo()
},
{
"[email protected]",
new EndOfWriteRequestInfo()
},
{
"[email protected]",
new KeepAliveRequestInfo()
}
};
Host = host;
Port = port;
Username = username;
ProxyType = proxyType;
ProxyHost = proxyHost;
ProxyPort = proxyPort;
ProxyUsername = proxyUsername;
ProxyPassword = proxyPassword;
AuthenticationMethods = authenticationMethods;
}
internal void Authenticate(ISession session, IServiceFactory serviceFactory)
{
ThrowHelper.ThrowIfNull(serviceFactory, "serviceFactory");
IsAuthenticated = false;
serviceFactory.CreateClientAuthentication().Authenticate(this, session);
IsAuthenticated = true;
}
void IConnectionInfoInternal.UserAuthenticationBannerReceived(object sender, MessageEventArgs<BannerMessage> e)
{
this.AuthenticationBanner?.Invoke(this, new AuthenticationBannerEventArgs(Username, e.Message.Message, e.Message.Language));
}
IAuthenticationMethod IConnectionInfoInternal.CreateNoneAuthenticationMethod()
{
return new NoneAuthenticationMethod(Username);
}
}
public class ExpectAction
{
public Regex Expect { get; private set; }
public Action<string> Action { get; private set; }
public ExpectAction(Regex expect, Action<string> action)
{
ThrowHelper.ThrowIfNull(expect, "expect");
ThrowHelper.ThrowIfNull(action, "action");
Expect = expect;
Action = action;
}
public ExpectAction(string expect, Action<string> action)
{
ThrowHelper.ThrowIfNull(expect, "expect");
ThrowHelper.ThrowIfNull(action, "action");
Expect = new Regex(Regex.Escape(expect));
Action = action;
}
}
public abstract class ForwardedPort : IForwardedPort, IDisposable
{
internal ISession Session { get; set; }
public abstract bool IsStarted { get; }
public event EventHandler Closing;
public event EventHandler<ExceptionEventArgs> Exception;
public event EventHandler<PortForwardEventArgs> RequestReceived;
public virtual void Start()
{
CheckDisposed();
if (IsStarted)
{
throw new InvalidOperationException("Forwarded port is already started.");
}
if (Session == null)
{
throw new InvalidOperationException("Forwarded port is not added to a client.");
}
if (!Session.IsConnected)
{
throw new SshConnectionException("Client not connected.");
}
Session.ErrorOccured += Session_ErrorOccured;
StartPort();
}
public virtual void Stop()
{
if (IsStarted)
{
StopPort(Session.ConnectionInfo.Timeout);
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected abstract void StartPort();
protected virtual void StopPort(TimeSpan timeout)
{
timeout.EnsureValidTimeout("timeout");
RaiseClosing();
ISession session = Session;
if (session != null)
{
session.ErrorOccured -= Session_ErrorOccured;
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
ISession session = Session;
if (session != null)
{
StopPort(session.ConnectionInfo.Timeout);
Session = null;
}
}
}
protected abstract void CheckDisposed();
protected void RaiseExceptionEvent(Exception exception)
{
this.Exception?.Invoke(this, new ExceptionEventArgs(exception));
}
protected void RaiseRequestReceived(string host, uint port)
{
this.RequestReceived?.Invoke(this, new PortForwardEventArgs(host, port));
}
private void RaiseClosing()
{
this.Closing?.Invoke(this, EventArgs.Empty);
}
private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
{
RaiseExceptionEvent(e.Exception);
}
}
public class ForwardedPortDynamic : ForwardedPort
{
private ForwardedPortStatus _status;
private bool _isDisposed;
private Socket _listener;
private CountdownEvent _pendingChannelCountdown;
public string BoundHost { get; }
public uint BoundPort { get; }
public override bool IsStarted => _status == ForwardedPortStatus.Started;
public ForwardedPortDynamic(uint port)
: this(string.Empty, port)
{
}
public ForwardedPortDynamic(string host, uint port)
{
BoundHost = host;
BoundPort = port;
_status = ForwardedPortStatus.Stopped;
}
protected override void StartPort()
{
if (!ForwardedPortStatus.ToStarting(ref _status))
{
return;
}
try
{
InternalStart();
}
catch (Exception)
{
_status = ForwardedPortStatus.Stopped;
throw;
}
}
protected override void StopPort(TimeSpan timeout)
{
timeout.EnsureValidTimeout("timeout");
if (ForwardedPortStatus.ToStopping(ref _status))
{
base.StopPort(timeout);
StopListener();
InternalStop(timeout);
_status = ForwardedPortStatus.Stopped;
}
}
protected override void CheckDisposed()
{
ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
}
protected override void Dispose(bool disposing)
{
if (!_isDisposed)
{
base.Dispose(disposing);
InternalDispose(disposing);
_isDisposed = true;
}
}
private void InternalStart()
{
InitializePendingChannelCountdown();
IPAddress address = IPAddress.Any;
if (!string.IsNullOrEmpty(BoundHost))
{
address = Dns.GetHostAddresses(BoundHost)[0];
}
IPEndPoint iPEndPoint = new IPEndPoint(address, (int)BoundPort);
_listener = new Socket(iPEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
{
NoDelay = true
};
_listener.Bind(iPEndPoint);
_listener.Listen(5);
base.Session.ErrorOccured += Session_ErrorOccured;
base.Session.Disconnected += Session_Disconnected;
_status = ForwardedPortStatus.Started;
StartAccept(null);
}
private void StartAccept(SocketAsyncEventArgs e)
{
if (e == null)
{
e = new SocketAsyncEventArgs();
e.Completed += AcceptCompleted;
}
else
{
e.AcceptSocket = null;
}
if (!IsStarted)
{
return;
}
try
{
if (!_listener.AcceptAsync(e))
{
AcceptCompleted(null, e);
}
}
catch (ObjectDisposedException)
{
if (_status == ForwardedPortStatus.Stopping || _status == ForwardedPortStatus.Stopped)
{
return;
}
throw;
}
}
private void AcceptCompleted(object sender, SocketAsyncEventArgs e)
{
SocketError socketError = e.SocketError;
if ((socketError != SocketError.OperationAborted && socketError != SocketError.NotSocket) || 1 == 0)
{
Socket acceptSocket = e.AcceptSocket;
if (e.SocketError != 0)
{
StartAccept(e);
CloseClientSocket(acceptSocket);
}
else
{
StartAccept(e);
ProcessAccept(acceptSocket);
}
}
}
private void ProcessAccept(Socket clientSocket)
{
if (!IsStarted)
{
CloseClientSocket(clientSocket);
return;
}
CountdownEvent pendingChannelCountdown = _pendingChannelCountdown;
pendingChannelCountdown.AddCount();
try
{
using IChannelDirectTcpip channelDirectTcpip = base.Session.CreateChannelDirectTcpip();
channelDirectTcpip.Exception += Channel_Exception;
if (!HandleSocks(channelDirectTcpip, clientSocket, base.Session.ConnectionInfo.Timeout))
{
CloseClientSocket(clientSocket);
}
else
{
channelDirectTcpip.Bind();
}
}
catch (Exception exception)
{
RaiseExceptionEvent(exception);
CloseClientSocket(clientSocket);
}
finally
{
try
{
pendingChannelCountdown.Signal();
}
catch (ObjectDisposedException)
{
}
}
}
private void InitializePendingChannelCountdown()
{
Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1))?.Dispose();
}
private bool HandleSocks(IChannelDirectTcpip channel, Socket clientSocket, TimeSpan timeout)
{
base.Closing += closeClientSocket;
try
{
int num = SocketAbstraction.ReadByte(clientSocket, timeout);
return num switch
{
-1 => false,
4 => HandleSocks4(clientSocket, channel, timeout),
5 => HandleSocks5(clientSocket, channel, timeout),
_ => throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "SOCKS version {0} is not supported.", num)),
};
}
catch (SocketException ex)
{
if (ex.SocketErrorCode != SocketError.ConnectionAborted)
{
RaiseExceptionEvent(ex);
}
return false;
}
finally
{
base.Closing -= closeClientSocket;
}
void closeClientSocket(object sender, EventArgs args)
{
CloseClientSocket(clientSocket);
}
}
private static void CloseClientSocket(Socket clientSocket)
{
if (clientSocket.Connected)
{
try
{
clientSocket.Shutdown(SocketShutdown.Send);
}
catch (Exception)
{
}
}
clientSocket.Dispose();
}
private void StopListener()
{
_listener?.Dispose();
ISession session = base.Session;
if (session != null)
{
session.ErrorOccured -= Session_ErrorOccured;
session.Disconnected -= Session_Disconnected;
}
}
private void InternalStop(TimeSpan timeout)
{
_pendingChannelCountdown.Signal();
_pendingChannelCountdown.Wait(timeout);
}
private void InternalDispose(bool disposing)
{
if (disposing)
{
Socket listener = _listener;
if (listener != null)
{
_listener = null;
listener.Dispose();
}
CountdownEvent pendingChannelCountdown = _pendingChannelCountdown;
if (pendingChannelCountdown != null)
{
_pendingChannelCountdown = null;
pendingChannelCountdown.Dispose();
}
}
}
private void Session_Disconnected(object sender, EventArgs e)
{
ISession session = base.Session;
if (session != null)
{
StopPort(session.ConnectionInfo.Timeout);
}
}
private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
{
ISession session = base.Session;
if (session != null)
{
StopPort(session.ConnectionInfo.Timeout);
}
}
private void Channel_Exception(object sender, ExceptionEventArgs e)
{
RaiseExceptionEvent(e.Exception);
}
private bool HandleSocks4(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
{
if (SocketAbstraction.ReadByte(socket, timeout) == -1)
{
return false;
}
byte[] array = new byte[2];
if (SocketAbstraction.Read(socket, array, 0, array.Length, timeout) == 0)
{
return false;
}
ushort port = BinaryPrimitives.ReadUInt16BigEndian(array);
byte[] array2 = new byte[4];
if (SocketAbstraction.Read(socket, array2, 0, array2.Length, timeout) == 0)
{
return false;
}
IPAddress iPAddress = new IPAddress(array2);
if (ReadString(socket, timeout) == null)
{
return false;
}
string text = iPAddress.ToString();
RaiseRequestReceived(text, port);
channel.Open(text, port, this, socket);
SocketAbstraction.SendByte(socket, 0);
if (channel.IsOpen)
{
SocketAbstraction.SendByte(socket, 90);
SocketAbstraction.Send(socket, array, 0, array.Length);
SocketAbstraction.Send(socket, array2, 0, array2.Length);
return true;
}
SocketAbstraction.SendByte(socket, 91);
return false;
}
private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
{
int num = SocketAbstraction.ReadByte(socket, timeout);
if (num == -1)
{
return false;
}
byte[] array = new byte[num];
if (SocketAbstraction.Read(socket, array, 0, array.Length, timeout) == 0)
{
return false;
}
if (array.Min() == 0)
{
SocketAbstraction.Send(socket, new byte[2] { 5, 0 }, 0, 2);
}
else
{
SocketAbstraction.Send(socket, new byte[2] { 5, 255 }, 0, 2);
}
switch (SocketAbstraction.ReadByte(socket, timeout))
{
case -1:
return false;
default:
throw new ProxyException("SOCKS5: Version 5 is expected.");
case 5:
if (SocketAbstraction.ReadByte(socket, timeout) == -1)
{
return false;
}
switch (SocketAbstraction.ReadByte(socket, timeout))
{
case -1:
return false;
default:
throw new ProxyException("SOCKS5: 0 is expected for reserved byte.");
case 0:
{
int num2 = SocketAbstraction.ReadByte(socket, timeout);
if (num2 == -1)
{
return false;
}
string socks5Host = GetSocks5Host(num2, socket, timeout);
if (socks5Host == null)
{
return false;
}
byte[] array2 = new byte[2];
if (SocketAbstraction.Read(socket, array2, 0, array2.Length, timeout) == 0)
{
return false;
}
ushort port = BinaryPrimitives.ReadUInt16BigEndian(array2);
RaiseRequestReceived(socks5Host, port);
channel.Open(socks5Host, port, this, socket);
byte[] array3 = CreateSocks5Reply(channel.IsOpen);
SocketAbstraction.Send(socket, array3, 0, array3.Length);
return true;
}
}
}
}
private static string GetSocks5Host(int addressType, Socket socket, TimeSpan timeout)
{
switch (addressType)
{
case 1:
{
byte[] array3 = new byte[4];
if (SocketAbstraction.Read(socket, array3, 0, 4, timeout) == 0)
{
return null;
}
return new IPAddress(array3).ToString();
}
case 3:
{
int num = SocketAbstraction.ReadByte(socket, timeout);
if (num == -1)
{
return null;
}
byte[] array2 = new byte[num];
if (SocketAbstraction.Read(socket, array2, 0, array2.Length, timeout) == 0)
{
return null;
}
return SshData.Ascii.GetString(array2, 0, array2.Length);
}
case 4:
{
byte[] array = new byte[16];
if (SocketAbstraction.Read(socket, array, 0, 16, timeout) == 0)
{
return null;
}
return new IPAddress(array).ToString();
}
default:
throw new ProxyException(string.Format(CultureInfo.InvariantCulture, "SOCKS5: Address type '{0}' is not supported.", addressType));
}
}
private static byte[] CreateSocks5Reply(bool channelOpen)
{
byte[] array = new byte[10] { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if (channelOpen)
{
array[1] = 0;
}
else
{
array[1] = 1;
}
array[2] = 0;
array[3] = 1;
return array;
}
private static string ReadString(Socket socket, TimeSpan timeout)
{
StringBuilder stringBuilder = new StringBuilder();
byte[] array = new byte[1];
while (true)
{
if (SocketAbstraction.Read(socket, array, 0, 1, timeout) == 0)
{
return null;
}
byte b = array[0];
if (b == 0)
{
break;
}
stringBuilder.Append((char)b);
}
return stringBuilder.ToString();
}
~ForwardedPortDynamic()
{
Dispose(disposing: false);
}
}
public class ForwardedPortLocal : ForwardedPort
{
private ForwardedPortStatus _status;
private bool _isDisposed;
private Socket _listener;
private CountdownEvent _pendingChannelCountdown;
public string BoundHost { get; private set; }
public uint BoundPort { get; private set; }
public string Host { get; private set; }
public uint Port { get; private set; }
public override bool IsStarted => _status == ForwardedPortStatus.Started;
public ForwardedPortLocal(uint boundPort, string host, uint port)
: this(string.Empty, boundPort, host, port)
{
}
public ForwardedPortLocal(string boundHost, string host, uint port)
: this(boundHost, 0u, host, port)
{
}
public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint port)
{
ThrowHelper.ThrowIfNull(boundHost, "boundHost");
ThrowHelper.ThrowIfNull(host, "host");
boundPort.ValidatePort("boundPort");
port.ValidatePort("port");
BoundHost = boundHost;
BoundPort = boundPort;
Host = host;
Port = port;
_status = ForwardedPortStatus.Stopped;
}
protected override void StartPort()
{
if (!ForwardedPortStatus.ToStarting(ref _status))
{
return;
}
try
{
InternalStart();
}
catch (Exception)
{
_status = ForwardedPortStatus.Stopped;
throw;
}
}
protected override void StopPort(TimeSpan timeout)
{
timeout.EnsureValidTimeout("timeout");
if (ForwardedPortStatus.ToStopping(ref _status))
{
base.StopPort(timeout);
StopListener();
InternalStop(timeout);
_status = ForwardedPortStatus.Stopped;
}
}
protected override void CheckDisposed()
{
ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
}
protected override void Dispose(bool disposing)
{
if (!_isDisposed)
{
base.Dispose(disposing);
InternalDispose(disposing);
_isDisposed = true;
}
}
~ForwardedPortLocal()
{
Dispose(disposing: false);
}
private void InternalStart()
{
IPEndPoint iPEndPoint = new IPEndPoint(Dns.GetHostAddresses(BoundHost)[0], (int)BoundPort);
_listener = new Socket(iPEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
{
NoDelay = true
};
_listener.Bind(iPEndPoint);
_listener.Listen(5);
BoundPort = (uint)((IPEndPoint)_listener.LocalEndPoint).Port;
base.Session.ErrorOccured += Session_ErrorOccured;
base.Session.Disconnected += Session_Disconnected;
InitializePendingChannelCountdown();
_status = ForwardedPortStatus.Started;
StartAccept(null);
}
private void StartAccept(SocketAsyncEventArgs e)
{
if (e == null)
{
e = new SocketAsyncEventArgs();
e.Completed += AcceptCompleted;
}
else
{
e.AcceptSocket = null;
}
if (!IsStarted)
{
return;
}
try
{
if (!_listener.AcceptAsync(e))
{
AcceptCompleted(null, e);
}
}
catch (ObjectDisposedException)
{
if (_status == ForwardedPortStatus.Stopping || _status == ForwardedPortStatus.Stopped)
{
return;
}
throw;
}
}
private void AcceptCompleted(object sender, SocketAsyncEventArgs e)
{
SocketError socketError = e.SocketError;
if ((socketError != SocketError.OperationAborted && socketError != SocketError.NotSocket) || 1 == 0)
{
Socket acceptSocket = e.AcceptSocket;
if (e.SocketError != 0)
{
StartAccept(e);
CloseClientSocket(acceptSocket);
}
else
{
StartAccept(e);
ProcessAccept(acceptSocket);
}
}
}
private void ProcessAccept(Socket clientSocket)
{
if (!IsStarted)
{
CloseClientSocket(clientSocket);
return;
}
CountdownEvent pendingChannelCountdown = _pendingChannelCountdown;
pendingChannelCountdown.AddCount();
try
{
IPEndPoint iPEndPoint = (IPEndPoint)clientSocket.RemoteEndPoint;
RaiseRequestReceived(iPEndPoint.Address.ToString(), (uint)iPEndPoint.Port);
using IChannelDirectTcpip channelDirectTcpip = base.Session.CreateChannelDirectTcpip();
channelDirectTcpip.Exception += Channel_Exception;
channelDirectTcpip.Open(Host, Port, this, clientSocket);
channelDirectTcpip.Bind();
}
catch (Exception exception)
{
RaiseExceptionEvent(exception);
CloseClientSocket(clientSocket);
}
finally
{
try
{
pendingChannelCountdown.Signal();
}
catch (ObjectDisposedException)
{
}
}
}
private void InitializePendingChannelCountdown()
{
Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1))?.Dispose();
}
private static void CloseClientSocket(Socket clientSocket)
{
if (clientSocket.Connected)
{
try
{
clientSocket.Shutdown(SocketShutdown.Send);
}
catch (Exception)
{
}
}
clientSocket.Dispose();
}
private void StopListener()
{
_listener?.Dispose();
ISession session = base.Session;
if (session != null)
{
session.ErrorOccured -= Session_ErrorOccured;
session.Disconnected -= Session_Disconnected;
}
}
private void InternalStop(TimeSpan timeout)
{
_pendingChannelCountdown.Signal();
_pendingChannelCountdown.Wait(timeout);
}
private void InternalDispose(bool disposing)
{
if (disposing)
{
Socket listener = _listener;
if (listener != null)
{
_listener = null;
listener.Dispose();
}
CountdownEvent pendingChannelCountdown = _pendingChannelCountdown;
if (pendingChannelCountdown != null)
{
_pendingChannelCountdown = null;
pendingChannelCountdown.Dispose();
}
}
}
private void Session_Disconnected(object sender, EventArgs e)
{
ISession session = base.Session;
if (session != null)
{
StopPort(session.ConnectionInfo.Timeout);
}
}
private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
{
ISession session = base.Session;
if (session != null)
{
StopPort(session.ConnectionInfo.Timeout);
}
}
private void Channel_Exception(object sender, ExceptionEventArgs e)
{
RaiseExceptionEvent(e.Exception);
}
}
public class ForwardedPortRemote : ForwardedPort
{
private ForwardedPortStatus _status;
private bool _requestStatus;
private EventWaitHandle _globalRequestResponse = new AutoResetEvent(initialState: false);
private CountdownEvent _pendingChannelCountdown;
private bool _isDisposed;
public override bool IsStarted => _status == ForwardedPortStatus.Started;
public IPAddress BoundHostAddress { get; private set; }
public string BoundHost => BoundHostAddress.ToString();
public uint BoundPort { get; private set; }
public IPAddress HostAddress { get; private set; }
public string Host => HostAddress.ToString();
public uint Port { get; private set; }
public ForwardedPortRemote(IPAddress boundHostAddress, uint boundPort, IPAddress hostAddress, uint port)
{
ThrowHelper.ThrowIfNull(boundHostAddress, "boundHostAddress");
ThrowHelper.ThrowIfNull(hostAddress, "hostAddress");
boundPort.ValidatePort("boundPort");
port.ValidatePort("port");
BoundHostAddress = boundHostAddress;
BoundPort = boundPort;
HostAddress = hostAddress;
Port = port;
_status = ForwardedPortStatus.Stopped;
}
public ForwardedPortRemote(uint boundPort, string host, uint port)
: this(string.Empty, boundPort, host, port)
{
}
public ForwardedPortRemote(string boundHost, uint boundPort, string host, uint port)
: this(Dns.GetHostAddresses(boundHost)[0], boundPort, Dns.GetHostAddresses(host)[0], port)
{
}
protected override void StartPort()
{
if (!ForwardedPortStatus.ToStarting(ref _status))
{
return;
}
InitializePendingChannelCountdown();
try
{
base.Session.RegisterMessage("SSH_MSG_REQUEST_FAILURE");
base.Session.RegisterMessage("SSH_MSG_REQUEST_SUCCESS");
base.Session.RegisterMessage("SSH_MSG_CHANNEL_OPEN");
base.Session.RequestSuccessReceived += Session_RequestSuccess;
base.Session.RequestFailureReceived += Session_RequestFailure;
base.Session.ChannelOpenReceived += Session_ChannelOpening;
base.Session.SendMessage(new TcpIpForwardGlobalRequestMessage(BoundHost, BoundPort));
base.Session.WaitOnHandle(_globalRequestResponse);
if (!_requestStatus)
{
throw new SshException(string.Format(CultureInfo.CurrentCulture, "Port forwarding for '{0}' port '{1}' failed to start.", Host, Port));
}
}
catch (Exception)
{
_status = ForwardedPortStatus.Stopped;
base.Session.RequestSuccessReceived -= Session_RequestSuccess;
base.Session.RequestFailureReceived -= Session_RequestFailure;
base.Session.ChannelOpenReceived -= Session_ChannelOpening;
throw;
}
_status = ForwardedPortStatus.Started;
}
protected override void StopPort(TimeSpan timeout)
{
timeout.EnsureValidTimeout("timeout");
if (ForwardedPortStatus.ToStopping(ref _status))
{
base.StopPort(timeout);
base.Session.SendMessage(new CancelTcpIpForwardGlobalRequestMessage(BoundHost, BoundPort));
WaitHandle.WaitAny(new WaitHandle[2]
{
_globalRequestResponse,
base.Session.MessageListenerCompleted
}, timeout);
base.Session.RequestSuccessReceived -= Session_RequestSuccess;
base.Session.RequestFailureReceived -= Session_RequestFailure;
base.Session.ChannelOpenReceived -= Session_ChannelOpening;
_pendingChannelCountdown.Signal();
_pendingChannelCountdown.Wait(timeout);
_status = ForwardedPortStatus.Stopped;
}
}
protected override void CheckDisposed()
{
ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
}
private void Session_ChannelOpening(object sender, MessageEventArgs<ChannelOpenMessage> e)
{
ChannelOpenMessage channelOpenMessage = e.Message;
ChannelOpenInfo info2 = channelOpenMessage.Info;
ForwardedTcpipChannelInfo info = info2 as ForwardedTcpipChannelInfo;
if (info == null || !(info.ConnectedAddress == BoundHost) || info.ConnectedPort != BoundPort)
{
return;
}
if (!IsStarted)
{
base.Session.SendMessage(new ChannelOpenFailureMessage(channelOpenMessage.LocalChannelNumber, string.Empty, 1u));
return;
}
ThreadAbstraction.ExecuteThread(delegate
{
CountdownEvent pendingChannelCountdown = _pendingChannelCountdown;
pendingChannelCountdown.AddCount();
try
{
RaiseRequestReceived(info.OriginatorAddress, info.OriginatorPort);
using IChannelForwardedTcpip channelForwardedTcpip = base.Session.CreateChannelForwardedTcpip(channelOpenMessage.LocalChannelNumber, channelOpenMessage.InitialWindowSize, channelOpenMessage.MaximumPacketSize);
channelForwardedTcpip.Exception += Channel_Exception;
channelForwardedTcpip.Bind(new IPEndPoint(HostAddress, (int)Port), this);
}
catch (Exception exception)
{
RaiseExceptionEvent(exception);
}
finally
{
try
{
pendingChannelCountdown.Signal();
}
catch (ObjectDisposedException)
{
}
}
});
}
private void InitializePendingChannelCountdown()
{
Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1))?.Dispose();
}
private void Channel_Exception(object sender, ExceptionEventArgs exceptionEventArgs)
{
RaiseExceptionEvent(exceptionEventArgs.Exception);
}
private void Session_RequestFailure(object sender, EventArgs e)
{
_requestStatus = false;
_globalRequestResponse.Set();
}
private void Session_RequestSuccess(object sender, MessageEventArgs<RequestSuccessMessage> e)
{
_requestStatus = true;
if (BoundPort == 0)
{
BoundPort = (e.Message.BoundPort.HasValue ? e.Message.BoundPort.Value : 0u);
}
_globalRequestResponse.Set();
}
protected override void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
base.Dispose(disposing);
if (disposing)
{
ISession session = base.Session;
if (session != null)
{
base.Session = null;
session.RequestSuccessReceived -= Session_RequestSuccess;
session.RequestFailureReceived -= Session_RequestFailure;
session.ChannelOpenReceived -= Session_ChannelOpening;
}
EventWaitHandle globalRequestResponse = _globalRequestResponse;
if (globalRequestResponse != null)
{
_globalRequestResponse = null;
globalRequestResponse.Dispose();
}
CountdownEvent pendingChannelCountdown = _pendingChannelCountdown;
if (pendingChannelCountdown != null)
{
_pendingChannelCountdown = null;
pendingChannelCountdown.Dispose();
}
}
_isDisposed = true;
}
}
internal sealed class ForwardedPortStatus
{
public static readonly ForwardedPortStatus Stopped = new ForwardedPortStatus(1, "Stopped");
public static readonly ForwardedPortStatus Stopping = new ForwardedPortStatus(2, "Stopping");
public static readonly ForwardedPortStatus Started = new ForwardedPortStatus(3, "Started");
public static readonly ForwardedPortStatus Starting = new ForwardedPortStatus(4, "Starting");
private readonly int _value;
private readonly string _name;
private ForwardedPortStatus(int value, string name)
{
_value = value;
_name = name;
}
public override bool Equals(object obj)
{
if (this == obj)
{
return true;
}
if (!(obj is ForwardedPortStatus forwardedPortStatus))
{
return false;
}
return forwardedPortStatus._value == _value;
}
public static bool operator ==(ForwardedPortStatus left, ForwardedPortStatus right)
{
return left?.Equals(right) ?? ((object)right == null);
}
public static bool operator !=(ForwardedPortStatus left, ForwardedPortStatus right)
{
return !(left == right);
}
public override int GetHashCode()
{
return _value;
}
public override string ToString()
{
return _name;
}
public static bool ToStopping(ref ForwardedPortStatus status)
{
ForwardedPortStatus forwardedPortStatus = Interlocked.CompareExchange(ref status, Stopping, Started);
if (forwardedPortStatus == Stopping || forwardedPortStatus == Stopped)
{
return false;
}
if (status == Stopping)
{
return true;
}
forwardedPortStatus = Interlocked.CompareExchange(ref status, Stopping, Starting);
if (forwardedPortStatus == Stopping || forwardedPortStatus == Stopped)
{
return false;
}
if (status == Stopping)
{
return true;
}
throw new InvalidOperationException($"Forwarded port cannot transition from '{forwardedPortStatus}' to '{Stopping}'.");
}
public static bool ToStarting(ref ForwardedPortStatus status)
{
ForwardedPortStatus forwardedPortStatus = Interlocked.CompareExchange(ref status, Starting, Stopped);
if (forwardedPortStatus == Starting || forwardedPortStatus == Started)
{
return false;
}
if (status == Starting)
{
return true;
}
throw new InvalidOperationException($"Forwarded port cannot transition from '{forwardedPortStatus}' to '{Starting}'.");
}
}
public class HashInfo
{
public int KeySize { get; private set; }
public bool IsEncryptThenMAC { get; private set; }
public Func<byte[], HashAlgorithm> HashAlgorithm { get; private set; }
public HashInfo(int keySize, Func<byte[], HashAlgorithm> hash, bool isEncryptThenMAC = false)
{
HashInfo hashInfo = this;
KeySize = keySize;
HashAlgorithm = (byte[] key) => hash(key.Take(hashInfo.KeySize / 8));
IsEncryptThenMAC = isEncryptThenMAC;
}
}
internal interface IAuthenticationMethod
{
string[] AllowedAuthentications { get; }
string Name { get; }
AuthenticationResult Authenticate(ISession session);
}
public interface IBaseClient : IDisposable
{
ConnectionInfo ConnectionInfo { get; }
bool IsConnected { get; }
TimeSpan KeepAliveInterval { get; set; }
event EventHandler<ExceptionEventArgs> ErrorOccurred;
event EventHandler<HostKeyEventArgs> HostKeyReceived;
event EventHandler<SshIdentificationEventArgs>? ServerIdentificationReceived;
void Connect();
Task ConnectAsync(CancellationToken cancellationToken);
void Disconnect();
void SendKeepAlive();
}
internal interface IClientAuthentication
{
void Authenticate(IConnectionInfoInternal connectionInfo, ISession session);
}
internal interface IConnectionInfo
{
TimeSpan ChannelCloseTimeout { get; }
IDictionary<string, RequestInfo> ChannelRequests { get; }
Encoding Encoding { get; }
string Host { get; }
int Port { get; }
ProxyTypes ProxyType { get; }
string ProxyHost { get; }
int ProxyPort { get; }
string ProxyUsername { get; }
string ProxyPassword { get; }
int RetryAttempts { get; }
TimeSpan Timeout { get; }
event EventHandler<AuthenticationBannerEventArgs> AuthenticationBanner;
}
internal interface IConnectionInfoInternal : IConnectionInfo
{
IList<IAuthenticationMethod> AuthenticationMethods { get; }
void UserAuthenticationBannerReceived(object sender, MessageEventArgs<BannerMessage> e);
IAuthenticationMethod CreateNoneAuthenticationMethod();
}
public interface IForwardedPort : IDisposable
{
event EventHandler Closing;
}
public interface IPrivateKeySource
{
IReadOnlyCollection<HostAlgorithm> HostKeyAlgorithms { get; }
}
public interface IRemotePathTransformation
{
string Transform(string path);
}
internal interface IServiceFactory
{
IClientAuthentication CreateClientAuthentication();
INetConfSession CreateNetConfSession(ISession session, int operationTimeout);
ISession CreateSession(ConnectionInfo connectionInfo, ISocketFactory socketFactory);
ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory);
PipeStream CreatePipeStream();
IKeyExchange CreateKeyExchange(IDictionary<string, Func<IKeyExchange>> clientAlgorithms, string[] serverAlgorithms);
ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize);
ISftpResponseFactory CreateSftpResponseFactory();
ShellStream CreateShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues, int bufferSize);
ShellStream CreateShellStreamNoTerminal(ISession session, int bufferSize);
IRemotePathTransformation CreateRemotePathDoubleQuoteTransformation();
IConnector CreateConnector(IConnectionInfo connectionInfo, ISocketFactory socketFactory);
IProtocolVersionExchange CreateProtocolVersionExchange();
ISocketFactory CreateSocketFactory();
}
internal interface ISession : IDisposable
{
IConnectionInfo ConnectionInfo { get; }
bool IsConnected { get; }
SemaphoreSlim SessionSemaphore { get; }
WaitHandle MessageListenerCompleted { get; }
event EventHandler<MessageEventArgs<ChannelCloseMessage>> ChannelCloseReceived;
event EventHandler<MessageEventArgs<ChannelDataMessage>> ChannelDataReceived;
event EventHandler<MessageEventArgs<ChannelEofMessage>> ChannelEofReceived;
event EventHandler<MessageEventArgs<ChannelExtendedDataMessage>> ChannelExtendedDataReceived;
event EventHandler<MessageEventArgs<ChannelFailureMessage>> ChannelFailureReceived;
event EventHandler<MessageEventArgs<ChannelOpenConfirmationMessage>> ChannelOpenConfirmationReceived;
event EventHandler<MessageEventArgs<ChannelOpenFailureMessage>> ChannelOpenFailureReceived;
event EventHandler<MessageEventArgs<ChannelOpenMessage>> ChannelOpenReceived;
event EventHandler<MessageEventArgs<ChannelRequestMessage>> ChannelRequestReceived;
event EventHandler<MessageEventArgs<ChannelSuccessMessage>> ChannelSuccessReceived;
event EventHandler<MessageEventArgs<ChannelWindowAdjustMessage>> ChannelWindowAdjustReceived;
event EventHandler<EventArgs> Disconnected;
event EventHandler<ExceptionEventArgs> ErrorOccured;
event EventHandler<SshIdentificationEventArgs> ServerIdentificationReceived;
event EventHandler<HostKeyEventArgs> HostKeyReceived;
event EventHandler<MessageEventArgs<RequestSuccessMessage>> RequestSuccessReceived;
event EventHandler<MessageEventArgs<RequestFailureMessage>> RequestFailureReceived;
event EventHandler<MessageEventArgs<BannerMessage>> UserAuthenticationBannerReceived;
void Connect();
Task ConnectAsync(CancellationToken cancellationToken);
IChannelSession CreateChannelSession();
IChannelDirectTcpip CreateChannelDirectTcpip();
IChannelForwardedTcpip CreateChannelForwardedTcpip(uint remoteChannelNumber, uint remoteWindowSize, uint remoteChannelDataPacketSize);
void Disconnect();
void OnDisconnecting();
void RegisterMessage(string messageName);
void SendMessage(Message message);
bool TrySendMessage(Message message);
void UnRegisterMessage(string messageName);
void WaitOnHandle(WaitHandle waitHandle);
void WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout);
WaitResult TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception);
WaitResult TryWait(WaitHandle waitHandle, TimeSpan timeout);
}
public interface ISftpClient : IBaseClient, IDisposable
{
uint BufferSize { get; set; }
TimeSpan OperationTimeout { get; set; }
int ProtocolVersion { get; }
string WorkingDirectory { get; }
void AppendAllLines(string path, IEnumerable<string> contents);
void AppendAllLines(string path, IEnumerable<string> contents, Encoding encoding);
void AppendAllText(string path, string contents);
void AppendAllText(string path, string contents, Encoding encoding);
StreamWriter AppendText(string path);
StreamWriter AppendText(string path, Encoding encoding);
IAsyncResult BeginDownloadFile(string path, Stream output);
IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback? asyncCallback);
IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback? asyncCallback, object? state, Action<ulong>? downloadCallback = null);
IAsyncResult BeginListDirectory(string path, AsyncCallback? asyncCallback, object? state, Action<int>? listCallback = null);
IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback? asyncCallback, object? state);
IAsyncResult BeginUploadFile(Stream input, string path);
IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback? asyncCallback);
IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback? asyncCallback, object? state, Action<ulong>? uploadCallback = null);
IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback? asyncCallback, object? state, Action<ulong>? uploadCallback = null);
void ChangeDirectory(string path);
Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default(CancellationToken));
void ChangePermissions(string path, short mode);
SftpFileStream Create(string path);
SftpFileStream Create(string path, int bufferSize);
void CreateDirectory(string path);
Task CreateDirectoryAsync(string path, CancellationToken cancellationToken = default(CancellationToken));
StreamWriter CreateText(string path);
StreamWriter CreateText(string path, Encoding encoding);
void Delete(string path);
Task DeleteAsync(string path, CancellationToken cancellationToken = default(CancellationToken));
void DeleteDirectory(string path);
Task DeleteDirectoryAsync(string path, CancellationToken cancellationToken = default(CancellationToken));
void DeleteFile(string path);
Task DeleteFileAsync(string path, CancellationToken cancellationToken);
void DownloadFile(string path, Stream output, Action<ulong>? downloadCallback = null);
void EndDownloadFile(IAsyncResult asyncResult);
IEnumerable<ISftpFile> EndListDirectory(IAsyncResult asyncResult);
IEnumerable<FileInfo> EndSynchronizeDirectories(IAsyncResult asyncResult);
void EndUploadFile(IAsyncResult asyncResult);
bool Exists(string path);
ISftpFile Get(string path);
SftpFileAttributes GetAttributes(string path);
DateTime GetLastAccessTime(string path);
DateTime GetLastAccessTimeUtc(string path);
DateTime GetLastWriteTime(string path);
DateTime GetLastWriteTimeUtc(string path);
SftpFileSystemInformation GetStatus(string path);
Task<SftpFileSystemInformation> GetStatusAsync(string path, CancellationToken cancellationToken);
IEnumerable<ISftpFile> ListDirectory(string path, Action<int>? listCallback = null);
IAsyncEnumerable<ISftpFile> ListDirectoryAsync(string path, CancellationToken cancellationToken);
SftpFileStream Open(string path, FileMode mode);
SftpFileStream Open(string path, FileMode mode, FileAccess access);
Task<SftpFileStream> OpenAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken);
SftpFileStream OpenRead(string path);
StreamReader OpenText(string path);
SftpFileStream OpenWrite(string path);
byte[] ReadAllBytes(string path);
string[] ReadAllLines(string path);
string[] ReadAllLines(string path, Encoding encoding);
string ReadAllText(string path);
string ReadAllText(string path, Encoding encoding);
IEnumerable<string> ReadLines(string path);
IEnumerable<string> ReadLines(string path, Encoding encoding);
void RenameFile(string oldPath, string newPath);
void RenameFile(string oldPath, string newPath, bool isPosix);
Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken);
void SetLastAccessTime(string path, DateTime lastAccessTime);
void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc);
void SetLastWriteTime(string path, DateTime lastWriteTime);
void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc);
void SetAttributes(string path, SftpFileAttributes fileAttributes);
void SymbolicLink(string path, string linkPath);
IEnumerable<FileInfo> SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern);
void UploadFile(Stream input, string path, Action<ulong>? uploadCallback = null);
void UploadFile(Stream input, string path, bool canOverride, Action<ulong>? uploadCallback = null);
void WriteAllBytes(string path, byte[] bytes);
void WriteAllLines(string path, IEnumerable<string> contents);
void WriteAllLines(string path, IEnumerable<string> contents, Encoding encoding);
void WriteAllLines(string path, string[] contents);
void WriteAllLines(string path, string[] contents, Encoding encoding);
void WriteAllText(string path, string contents);
void WriteAllText(string path, string contents, Encoding encoding);
}
public interface ISshClient : IBaseClient, IDisposable
{
IEnumerable<ForwardedPort> ForwardedPorts { get; }
void AddForwardedPort(ForwardedPort port);
void RemoveForwardedPort(ForwardedPort port);
SshCommand CreateCommand(string commandText);
SshCommand CreateCommand(string commandText, Encoding encoding);
SshCommand RunCommand(string commandText);
Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint>? terminalModes, int bufferSize);
Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModes);
Shell CreateShell(Stream input, Stream output, Stream extendedOutput);
Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint>? terminalModes, int bufferSize);
Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModes);
Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput);
Shell CreateShellNoTerminal(Stream input, Stream output, Stream extendedOutput, int bufferSize = -1);
ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize);
ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary<TerminalModes, uint>? terminalModeValues);
ShellStream CreateShellStreamNoTerminal(int bufferSize = -1);
}
internal interface ISubsystemSession : IDisposable
{
int OperationTimeout { get; set; }
bool IsOpen { get; }
void Connect();
void Disconnect();
void WaitOnHandle(WaitHandle waitHandle, int millisecondsTimeout);
bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout);
int WaitAny(WaitHandle waitHandleA, WaitHandle waitHandleB, int millisecondsTimeout);
int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout);
WaitHandle[] CreateWaitHandleArray(params WaitHandle[] waitHandles);
WaitHandle[] CreateWaitHandleArray(WaitHandle waitHandle1, WaitHandle waitHandle2);
}
public class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod, IDisposable
{
private readonly RequestMessageKeyboardInteractive _requestMessage;
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure;
private Session _session;
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(initialState: false);
private Exception _exception;
private bool _isDisposed;
public override string Name => _requestMessage.MethodName;
public event EventHandler<AuthenticationPromptEventArgs> AuthenticationPrompt;
public KeyboardInteractiveAuthenticationMethod(string username)
: base(username)
{
_requestMessage = new RequestMessageKeyboardInteractive(ServiceName.Connection, username);
}
public override AuthenticationResult Authenticate(Session session)
{
_session = session;
session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived;
session.UserAuthenticationInformationRequestReceived += Session_UserAuthenticationInformationRequestReceived;
session.RegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST");
try
{
session.SendMessage(_requestMessage);
session.WaitOnHandle(_authenticationCompleted);
}
finally
{
session.UnRegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST");
session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived;
session.UserAuthenticationInformationRequestReceived -= Session_UserAuthenticationInformationRequestReceived;
}
if (_exception != null)
{
ExceptionDispatchInfo.Capture(_exception).Throw();
}
return _authenticationResult;
}
private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs<SuccessMessage> e)
{
_authenticationResult = AuthenticationResult.Success;
_authenticationCompleted.Set();
}
private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
if (e.Message.PartialSuccess)
{
_authenticationResult = AuthenticationResult.PartialSuccess;
}
else
{
_authenticationResult = AuthenticationResult.Failure;
}
base.AllowedAuthentications = e.Message.AllowedAuthentications;
_authenticationCompleted.Set();
}
private void Session_UserAuthenticationInformationRequestReceived(object sender, MessageEventArgs<InformationRequestMessage> e)
{
InformationRequestMessage message = e.Message;
AuthenticationPromptEventArgs eventArgs = new AuthenticationPromptEventArgs(base.Username, message.Instruction, message.Language, message.Prompts);
ThreadAbstraction.ExecuteThread(delegate
{
try
{
this.AuthenticationPrompt?.Invoke(this, eventArgs);
InformationResponseMessage informationResponseMessage = new InformationResponseMessage();
foreach (AuthenticationPrompt item in eventArgs.Prompts.OrderBy((AuthenticationPrompt r) => r.Id))
{
if (item.Response == null)
{
throw new SshAuthenticationException("AuthenticationPrompt.Response is null for prompt \"" + item.Request + "\". You can set this by subscribing to KeyboardInteractiveAuthenticationMethod.AuthenticationPrompt and inspecting the Prompts property of the event args.");
}
informationResponseMessage.Responses.Add(item.Response);
}
_session.SendMessage(informationResponseMessage);
}
catch (Exception exception)
{
_exception = exception;
_authenticationCompleted.Set();
}
});
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed && disposing)
{
EventWaitHandle authenticationCompleted = _authenticationCompleted;
if (authenticationCompleted != null)
{
_authenticationCompleted = null;
authenticationCompleted.Dispose();
}
_isDisposed = true;
}
}
}
public class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable
{
private bool _isDisposed;
public event EventHandler<AuthenticationPromptEventArgs> AuthenticationPrompt;
public KeyboardInteractiveConnectionInfo(string host, string username)
: this(host, 22, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty)
{
}
public KeyboardInteractiveConnectionInfo(string host, int port, string username)
: this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty)
{
}
public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort)
: this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty)
{
}
public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername)
: this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty)
{
}
public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort)
: this(host, 22, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty)
{
}
public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername)
: this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty)
{
}
public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword)
: this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword)
{
}
public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword)
: base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new KeyboardInteractiveAuthenticationMethod(username))
{
foreach (AuthenticationMethod authenticationMethod in base.AuthenticationMethods)
{
if (authenticationMethod is KeyboardInteractiveAuthenticationMethod keyboardInteractiveAuthenticationMethod)
{
keyboardInteractiveAuthenticationMethod.AuthenticationPrompt += AuthenticationMethod_AuthenticationPrompt;
}
}
}
private void AuthenticationMethod_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
{
this.AuthenticationPrompt?.Invoke(sender, e);
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_isDisposed || !disposing)
{
return;
}
if (base.AuthenticationMethods != null)
{
foreach (AuthenticationMethod authenticationMethod in base.AuthenticationMethods)
{
if (authenticationMethod is IDisposable disposable)
{
disposable.Dispose();
}
}
}
_isDisposed = true;
}
}
public class MessageEventArgs<T> : EventArgs
{
public T Message { get; private set; }
public MessageEventArgs(T message)
{
ThrowHelper.ThrowIfNull(message, "message");
Message = message;
}
}
public class NetConfClient : BaseClient
{
private int _operationTimeout;
private INetConfSession? _netConfSession;
public TimeSpan OperationTimeout
{
get
{
return TimeSpan.FromMilliseconds(_operationTimeout);
}
set
{
_operationTimeout = value.AsTimeout("OperationTimeout");
}
}
internal INetConfSession? NetConfSession => _netConfSession;
public XmlDocument ServerCapabilities
{
get
{
if (_netConfSession == null)
{
throw new SshConnectionException("Client not connected.");
}
return _netConfSession.ServerCapabilities;
}
}
public XmlDocument ClientCapabilities
{
get
{
if (_netConfSession == null)
{
throw new SshConnectionException("Client not connected.");
}
return _netConfSession.ClientCapabilities;
}
}
public bool AutomaticMessageIdHandling { get; set; }
public NetConfClient(ConnectionInfo connectionInfo)
: this(connectionInfo, ownsConnectionInfo: false)
{
}
public NetConfClient(string host, int port, string username, string password)
: this(new PasswordConnectionInfo(host, port, username, password), ownsConnectionInfo: true)
{
}
public NetConfClient(string host, string username, string password)
: this(host, 22, username, password)
{
}
public NetConfClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
: this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
{
}
public NetConfClient(string host, string username, params IPrivateKeySource[] keyFiles)
: this(host, 22, username, keyFiles)
{
}
private NetConfClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo)
: this(connectionInfo, ownsConnectionInfo, new ServiceFactory())
{
}
internal NetConfClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory)
: base(connectionInfo, ownsConnectionInfo, serviceFactory)
{
_operationTimeout = -1;
AutomaticMessageIdHandling = true;
}
public XmlDocument SendReceiveRpc(XmlDocument rpc)
{
if (_netConfSession == null)
{
throw new SshConnectionException("Client not connected.");
}
return _netConfSession.SendReceiveRpc(rpc, AutomaticMessageIdHandling);
}
public XmlDocument SendReceiveRpc(string xml)
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
return SendReceiveRpc(xmlDocument);
}
public XmlDocument SendCloseRpc()
{
if (_netConfSession == null)
{
throw new SshConnectionException("Client not connected.");
}
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rpc message-id=\"6666\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><close-session/></rpc>");
return _netConfSession.SendReceiveRpc(xmlDocument, AutomaticMessageIdHandling);
}
protected override void OnConnected()
{
base.OnConnected();
_netConfSession = CreateAndConnectNetConfSession();
}
protected override void OnDisconnecting()
{
base.OnDisconnecting();
_netConfSession?.Disconnect();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && _netConfSession != null)
{
_netConfSession.Dispose();
_netConfSession = null;
}
}
private INetConfSession CreateAndConnectNetConfSession()
{
INetConfSession netConfSession = base.ServiceFactory.CreateNetConfSession(base.Session, _operationTimeout);
try
{
netConfSession.Connect();
return netConfSession;
}
catch
{
netConfSession.Dispose();
throw;
}
}
}
public class NoneAuthenticationMethod : AuthenticationMethod, IDisposable
{
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure;
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(initialState: false);
private bool _isDisposed;
public override string Name => "none";
public NoneAuthenticationMethod(string username)
: base(username)
{
}
public override AuthenticationResult Authenticate(Session session)
{
ThrowHelper.ThrowIfNull(session, "session");
session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived;
try
{
session.SendMessage(new RequestMessageNone(ServiceName.Connection, base.Username));
session.WaitOnHandle(_authenticationCompleted);
}
finally
{
session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived;
}
return _authenticationResult;
}
private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs<SuccessMessage> e)
{
_authenticationResult = AuthenticationResult.Success;
_authenticationCompleted.Set();
}
private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs<FailureMessage> e)
{
if (e.Message.PartialSuccess)
{
_authenticationResult = AuthenticationResult.PartialSuccess;
}
else
{
_authenticationResult = AuthenticationResult.Failure;
}
base.AllowedAuthentications = e.Message.AllowedAuthentications;
_authenticationCompleted.Set();
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed && disposing)
{
EventWaitHandle authenticationCompleted = _authenticationCompleted;
if (authenticationCompleted != null)
{
authenticationCompleted.Dispose();
_authenticationCompleted = null;
}
_isDisposed = true;
}
}
}
public class PasswordAuthenticationMethod : AuthenticationMethod, IDisposable
{
private readonly RequestMessagePassword _requestMessage;
private readonly byte[] _password;
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure;
private Session _session;
private EventWaitHandle _authenticationCompleted = new AutoResetEvent(initialState: false);
private Exception _exception;
private bool _isDisposed;
public override string Name => _requestMessage.MethodName;
internal byte[] Password => _password;
public event EventHandler<AuthenticationPasswordChangeEventArgs> PasswordExpired;
public PasswordAuthenticationMethod(string username, string password)
: this(username, Encoding.UTF8.GetBytes(password))
{
}
public PasswordAuthenticationMethod(string username, byte[] password)
: base(username)
{
ThrowHelper.ThrowIfNull(password, "password");
_password = password;
_requestMessage = new RequestMessagePassword(ServiceName.Connection, base.Username, _password);
}
public override AuthenticationResult Authenticate(Session session)
{
ThrowHelper.ThrowIfNull(session, "session");
_session = session;
session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived;
session.UserAuthenticationPasswordChangeRequiredReceived += Session_UserAuthenticationPasswordChangeRequiredReceived;
try
{
session.RegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ");
session.SendMessage(_requestMessage);
session.WaitOnHandle(_authenticationCompleted);
}
finally
{
session.UnRegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ");
session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived;
session.UserAuthenticationFailur