<PackageReference Include="Relativity.Server.Transfer.SDK" Version="24000.0.1" />

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 CreateFileShareService(RelativityFileShareBase fileShare, SupportCheckResult result) { return new FileShareService(relativityConnectionInfo, configuration, fileShare.ArtifactId, transferLog, true); } [AsyncStateMachine(typeof(<GetFileShare>d__39))] private Task<RelativityFileShareBase> GetFileShare(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; } } }