AsperaDiagnosticsService
class AsperaDiagnosticsService
using Relativity.Transfer.Aspera.Resources;
using Renci.SshNet;
using Renci.SshNet.Common;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Relativity.Transfer.Aspera
{
internal class AsperaDiagnosticsService
{
public const int DefaultMaxTimeoutSeconds = 120;
public const string AsperaLogFileName = "aspera-scp-transfer.log";
private const int SuccessfulExitCode = 0;
private readonly AsperaClientConfiguration configuration;
private readonly DiagnosticsContext diagnosticContext;
private readonly IFileSystemService fileSystemService;
private readonly RelativityConnectionInfo relativityConnectionInfo;
private readonly AsperaCredential asperaCredential;
private readonly ITransferLog transferLog;
public int MaxTimeoutSeconds { get; set; }
public AsperaDiagnosticsService(RelativityConnectionInfo relativityConnectionInfo, AsperaClientConfiguration configuration, DiagnosticsContext context, IFileSystemService fileSystemService, ITransferLog log)
{
if (relativityConnectionInfo == null)
throw new ArgumentNullException("relativityConnectionInfo");
if (configuration == null)
throw new ArgumentNullException("configuration");
if (context == null)
throw new ArgumentNullException("context");
if (fileSystemService == null)
throw new ArgumentNullException("fileSystemService");
if (log == null)
throw new ArgumentNullException("log");
MaxTimeoutSeconds = 120;
this.relativityConnectionInfo = relativityConnectionInfo;
this.configuration = configuration;
diagnosticContext = context;
this.fileSystemService = fileSystemService;
transferLog = log;
asperaCredential = new AsperaCredential(configuration.Credential);
}
public Task<ISupportCheckResult> TestConnectivityAsync(CancellationToken token)
{
return TestConnectivityAsync(configuration.TestConnectionDestinationPath, token);
}
public Task<ISupportCheckResult> TestConnectivityAsync(string targetPath, CancellationToken token)
{
if (string.IsNullOrEmpty(targetPath))
throw new ArgumentNullException("targetPath");
<>c__DisplayClass15_0 <>4__this;
return Task.Run(delegate {
<>c__DisplayClass15_0.<<TestConnectivityAsync>b__0>d stateMachine = default(<>c__DisplayClass15_0.<<TestConnectivityAsync>b__0>d);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<ISupportCheckResult>.Create();
stateMachine.<>4__this = <>4__this;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}, token);
}
public Task<ISupportCheckResult> TestPortsAsync(CancellationToken token)
{
SupportCheckResult result = new SupportCheckResult();
return Task.Run((Func<ISupportCheckResult>)delegate {
TestPorts(result, token);
return result;
}, token);
}
public Task<ISupportCheckResult> TestFaspUploadAsync(TempDirectory tempDirectory, string sourcePath, string destinationPath, CancellationToken token)
{
<>c__DisplayClass17_0 <>4__this;
return Task.Run(delegate {
<>c__DisplayClass17_0.<<TestFaspUploadAsync>b__0>d stateMachine = default(<>c__DisplayClass17_0.<<TestFaspUploadAsync>b__0>d);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<ISupportCheckResult>.Create();
stateMachine.<>4__this = <>4__this;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}, token);
}
public Task<ISupportCheckResult> TestFaspDownloadAsync(TempDirectory tempDirectory, string sourcePath, string destinationPath, CancellationToken token)
{
<>c__DisplayClass18_0 <>4__this;
return Task.Run(delegate {
<>c__DisplayClass18_0.<<TestFaspDownloadAsync>b__0>d stateMachine = default(<>c__DisplayClass18_0.<<TestFaspDownloadAsync>b__0>d);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<ISupportCheckResult>.Create();
stateMachine.<>4__this = <>4__this;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}, token);
}
private static string GetTestUploadFileName()
{
return "kCuraTestConnectionFile_" + Environment.MachineName.ToUpperInvariant() + "_" + Environment.UserName.ToUpperInvariant() + ".bin";
}
private static string CreateTestUploadFile(TempDirectory tempDirectory, string fileName)
{
string text = Path.Combine(tempDirectory.Directory, fileName);
using (FileStream fileStream = new FileStream(text, FileMode.CreateNew)) {
fileStream.Seek(8192, SeekOrigin.Begin);
fileStream.WriteByte(13);
fileStream.Flush(true);
return text;
}
}
private string GetRemoteTestUploadFilePath(TransferPath path)
{
if (path.Direction == TransferDirection.Upload) {
string fileName = fileSystemService.GetFileName(path.SourcePath);
return PathHelper.CombineUnix(path.TargetPath, fileName);
}
return path.SourcePath;
}
[AsyncStateMachine(typeof(<PerformZeroByteTransferAsync>d__22))]
private Task PerformZeroByteTransferAsync(TempDirectory tempDirectory, TransferPath path, SupportCheckResult result, bool considerDeleteTestfile, CancellationToken token)
{
<PerformZeroByteTransferAsync>d__22 stateMachine = default(<PerformZeroByteTransferAsync>d__22);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.tempDirectory = tempDirectory;
stateMachine.path = path;
stateMachine.result = result;
stateMachine.considerDeleteTestfile = considerDeleteTestfile;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
private void TestSshPort(SupportCheckResult result, CancellationToken token)
{
if (!token.IsCancellationRequested) {
Uri hostName = GetHostName();
int tcpPort = configuration.TcpPort;
string accountUserName = GetAccountUserName();
string accountPassword = GetAccountPassword();
PasswordConnectionInfo val = new PasswordConnectionInfo(hostName.ToString(), tcpPort, accountUserName, accountPassword);
try {
val.set_Timeout(TimeSpan.FromSeconds((double)MaxTimeoutSeconds));
LogInformation($"""{tcpPort}""{hostName}""");
SshClient val2 = new SshClient(val);
try {
val2.Connect();
LogInformation($"""{tcpPort}""{hostName}""");
result.IsSupported = true;
} catch (Exception ex) {
string message = $"""{tcpPort}""{hostName}""";
result.IsSupported = false;
if (!(ex is SshAuthenticationException)) {
SocketException ex2 = ex as SocketException;
if (ex2 != null) {
result.IsSupported = false;
switch (ex2.SocketErrorCode) {
case SocketError.ConnectionRefused:
message = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedInvalidPortOrBlockedMessage, hostName, tcpPort);
break;
case SocketError.TimedOut:
message = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedInvalidPortOrHostOrServerDownMessage, hostName, tcpPort);
break;
default:
message = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedSshPortMessage, hostName, tcpPort);
break;
}
}
} else {
message = $"""{tcpPort}""{hostName}""";
result.IsSupported = false;
}
LogError(message, ex);
} finally {
((IDisposable)val2)?.Dispose();
}
} finally {
((IDisposable)val)?.Dispose();
}
}
}
private void TestTcpPort(SupportCheckResult result, CancellationToken token)
{
if (!token.IsCancellationRequested) {
Uri hostName = GetHostName();
int tcpPort = configuration.TcpPort;
LogInformation($"""{tcpPort}""{hostName}""");
TcpClient tcpClient = new TcpClient();
try {
tcpClient.Client.ReceiveTimeout = MaxTimeoutSeconds * 1000;
tcpClient.Connect(hostName.ToString(), tcpPort);
result.IsSupported = true;
LogInformation($"""{tcpPort}""{hostName}""");
} catch (Exception exception) {
LogError($"""{tcpPort}""{hostName}""", exception);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedTcpPortMessage, hostName, tcpPort);
} finally {
tcpClient.Close();
}
}
}
private void TestUdpPort(SupportCheckResult result, CancellationToken token)
{
if (!token.IsCancellationRequested) {
Uri host = asperaCredential.Host;
int udpPortStartRange = configuration.UdpPortStartRange;
LogInformation($"""{udpPortStartRange}""{host}""");
UdpClient udpClient = new UdpClient();
try {
udpClient.Client.ReceiveTimeout = MaxTimeoutSeconds * 1000;
udpClient.Connect(host.ToString(), udpPortStartRange);
LogInformation($"""{udpPortStartRange}""{host}""");
} catch (Exception exception) {
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedUdpPortMessage, GetHostName(), GetUdpPortRangeString());
LogError($"""{udpPortStartRange}""{host}""", exception);
} finally {
udpClient.Close();
}
}
}
private void TestPorts(SupportCheckResult result, CancellationToken token)
{
if (configuration.LegacyPortCheck)
TestPortsLegacy(result, token);
else
TestPortsNew(result, token);
}
private void TestPortsNew(SupportCheckResult result, CancellationToken token)
{
TestTcpPort(result, token);
if (result.IsSupported && !token.IsCancellationRequested) {
TestUdpPort(result, token);
if (result.IsSupported && !token.IsCancellationRequested)
result.IsSupported = true;
}
}
private void TestPortsLegacy(SupportCheckResult result, CancellationToken token)
{
TestSshPort(result, token);
if (result.IsSupported && !token.IsCancellationRequested) {
TestUdpPort(result, token);
if (result.IsSupported && !token.IsCancellationRequested)
result.IsSupported = true;
}
}
private int ExecProcess(TransferDirection direction, StringBuilder commandLine, AsperaRuntimePaths paths, TempDirectory tempDirectory, SupportCheckResult result)
{
DataReceivedEventHandler value = delegate(object sender, DataReceivedEventArgs args) {
if (!string.IsNullOrEmpty(args.Data))
result.AddStandardOutput(args.Data);
};
DataReceivedEventHandler value2 = delegate(object sender, DataReceivedEventArgs args) {
if (!string.IsNullOrEmpty(args.Data))
result.AddStandardError(args.Data);
};
using (Process process = new Process())
try {
string accountPassword = GetAccountPassword();
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.EnvironmentVariables["ASPERA_SCP_PASS"] = accountPassword;
processStartInfo.EnvironmentVariables["ASPERA_SCP_COOKIE"] = AsperaFaspService.EncodeCookieString("transfer-api-connection-check", Guid.NewGuid());
processStartInfo.FileName = paths.AsperaEngineFile;
processStartInfo.Arguments = commandLine.ToString();
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.WorkingDirectory = paths.BinDirectory;
process.StartInfo = processStartInfo;
process.EnableRaisingEvents = true;
process.OutputDataReceived += value;
process.ErrorDataReceived += value2;
LogInformation($"""{direction}""");
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
int num = 0;
try {
process.WaitForExit(MaxTimeoutSeconds * 1000);
if (!process.HasExited)
process.Kill();
num = process.ExitCode;
} catch (Exception ex) {
LogError($"""{direction}""{num}", ex);
throw new InvalidOperationException(AsperaStrings.AsperaTestConnectionProcessExceptionMessage, ex);
}
if (num == 0)
LogInformation($"""{direction}""{num}");
else
LogError($"""{direction}""{num}", null);
string text = Path.Combine(tempDirectory.Directory, "aspera-scp-transfer.log");
if (!File.Exists(text))
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, AsperaStrings.AsperaTestConnectionLogFileNotFoundExceptionMessage, text));
LogInformation("Preparing to retrieve the Aspera diagnostic logs from the '" + text + "' file...");
string[] array = File.ReadAllLines(text);
LogInformation($"""{array.Length}""");
string[] array2 = File.ReadAllLines(text);
foreach (string line in array2) {
result.AddLogFileLine(line);
}
if (num == 0) {
result.ErrorMessage = string.Empty;
result.IsSupported = true;
} else {
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFaspFailedMessage, GetHostName(), configuration.TcpPort, GetUdpPortRangeString());
result.IsSupported = false;
}
return num;
} catch (InvalidOperationException exception) {
LogError($"""{direction}""", exception);
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionEnvironmentExceptionMessage, GetHostName());
result.IsSupported = false;
return -1;
} catch (Exception exception2) {
LogError($"""{direction}""", exception2);
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionExceptionMessage, GetHostName());
result.IsSupported = false;
return -1;
} finally {
process.OutputDataReceived -= value;
process.ErrorDataReceived -= value2;
}
}
private string GetUdpPortRangeString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}-{1}", configuration.UdpPortStartRange, configuration.UdpPortEndRange);
}
private void LogError(string message, Exception exception = null)
{
transferLog.LogError(exception, "Aspera Test Connection - {Message}", message);
}
private void LogInformation(string message)
{
transferLog.LogInformation("Aspera Test Connection - {Message}", message);
}
private string GetAccountUserName()
{
string text = asperaCredential.AccountUserName.UnprotectData();
if (!string.IsNullOrEmpty(text))
return text;
throw new InvalidOperationException(AsperaStrings.AsperaTestConnectionUserNameExceptionMessage);
}
private string GetAccountPassword()
{
string text = asperaCredential.AccountPassword.UnprotectData();
if (!string.IsNullOrEmpty(text))
return text;
throw new InvalidOperationException(AsperaStrings.AsperaTestConnectionPasswordExceptionMessage);
}
private Uri GetHostName()
{
Uri host = asperaCredential.Host;
if (host != (Uri)null)
return host;
throw new InvalidOperationException(AsperaStrings.AsperaTestConnectionHostExceptionMessage);
}
[AsyncStateMachine(typeof(<TryVerifyUploadAsync>d__36))]
private Task TryVerifyUploadAsync(TransferPath testFilePath, SupportCheckResult result, CancellationToken token)
{
<TryVerifyUploadAsync>d__36 stateMachine = default(<TryVerifyUploadAsync>d__36);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.testFilePath = testFilePath;
stateMachine.result = result;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[AsyncStateMachine(typeof(<TryDeleteTestFile>d__37))]
private Task TryDeleteTestFile(TransferPath testFilePath, SupportCheckResult result, CancellationToken token)
{
<TryDeleteTestFile>d__37 stateMachine = default(<TryDeleteTestFile>d__37);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.testFilePath = testFilePath;
stateMachine.result = result;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
private FileShareService (RelativityFileShareBase fileShare, SupportCheckResult result)
{
return new FileShareService(relativityConnectionInfo, configuration, fileShare.ArtifactId, transferLog, true);
}
[AsyncStateMachine(typeof(<GetFileShare>d__39))]
private Task<RelativityFileShareBase> (SupportCheckResult result, CancellationToken token)
{
<GetFileShare>d__39 stateMachine = default(<GetFileShare>d__39);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<RelativityFileShareBase>.Create();
stateMachine.<>4__this = this;
stateMachine.result = result;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
}
}