From 0e3ba66b0e8defa0d67d4403ea4743a098e8184b Mon Sep 17 00:00:00 2001 From: Michael Koster Date: Tue, 30 Dec 2025 23:43:13 +0100 Subject: [PATCH 1/2] =?UTF-8?q?adding=20-4=20option=20only=20of=20ForceIPv?= =?UTF-8?q?=C3=A7=20is=20selected?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/RemoteDebuggerLauncher/PackageConstants.cs | 7 +++++-- .../RemoteOperations/SecureShellKeySetupService.cs | 7 +++++-- .../RemoteOperations/SecureShellKeySetupSettings.cs | 8 +++++++- .../RemoteOperations/SecureShellPassphraseDialog.xaml | 1 - 4 files changed, 17 insertions(+), 6 deletions(-) delete mode 100644 src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellPassphraseDialog.xaml diff --git a/src/Extension/RemoteDebuggerLauncher/PackageConstants.cs b/src/Extension/RemoteDebuggerLauncher/PackageConstants.cs index 22741ae3..6023f968 100644 --- a/src/Extension/RemoteDebuggerLauncher/PackageConstants.cs +++ b/src/Extension/RemoteDebuggerLauncher/PackageConstants.cs @@ -181,13 +181,16 @@ public static class SecureShell public const string DefaultKnownHostsFileName = "known_hosts"; /// SSH key scanner arguments. - public const string KeyScanArguments = "-4 -p {1} {0}"; + public const string KeyScanArguments = "{2}-p {1} {0}"; + + /// Argument for SSH to force IPv4 usage. + public const string SshForceIPv4 = "-4 "; /// SSH executable. public const string SshExecutable = "ssh.exe"; /// The SSH arguments to add server fingerprint to known_hosts file. - public const string SshArguments = "{0}@{1} -p {2} -i \"{3}\" \"echo DONE\""; + public const string SshArguments = "{0}@{1} {4}-p {2} -i \"{3}\" \"echo DONE\""; /// HTTPS Developer Certificate name. public const string HttpsCertificateName = "DevCert.pfx"; diff --git a/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellKeySetupService.cs b/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellKeySetupService.cs index bb189301..165e6e42 100644 --- a/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellKeySetupService.cs +++ b/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellKeySetupService.cs @@ -11,6 +11,7 @@ using System.IO; using System.Text; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; using RemoteDebuggerLauncher.Infrastructure; using Renci.SshNet; @@ -106,7 +107,7 @@ public async Task AuthorizeKeyAsync(SecureShellKeySetupSettings settings) var defaultKeysFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), PackageConstants.SecureShell.DefaultKeyPairFolder); var knownHostsFilePath = Path.Combine(defaultKeysFolder, PackageConstants.SecureShell.DefaultKnownHostsFileName); - var arguments = string.Format(PackageConstants.SecureShell.KeyScanArguments, settings.HostName, settings.HostPort); + var arguments = string.Format(PackageConstants.SecureShell.KeyScanArguments, settings.HostName, settings.HostPort, settings.ForceIPv4 ? PackageConstants.SecureShell.SshForceIPv4 : string.Empty); var startInfo = new ProcessStartInfo(PackageConstants.SecureShell.KeyScanExecutable, arguments) { CreateNoWindow = true, @@ -124,6 +125,8 @@ public async Task AuthorizeKeyAsync(SecureShellKeySetupSettings settings) var stdError = await process.StandardError.ReadToEndAsync(); var exitCode = await process.WaitForExitAsync(); + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + if (exitCode == 0) { if (FileHelper.ContainsText(knownHostsFilePath, settings.HostName)) @@ -155,7 +158,7 @@ private async Task RegisterServerFingerprintWithConnectionAsync(SecureShel const string SshDoneMarker = "DONE"; // open a pseudo console windows to run the ssh command - var arguments = string.Format(PackageConstants.SecureShell.SshArguments, settings.UserName, settings.HostName, settings.HostPort, settings.PrivateKeyFile); + var arguments = string.Format(PackageConstants.SecureShell.SshArguments, settings.UserName, settings.HostName, settings.HostPort, settings.PrivateKeyFile, settings.ForceIPv4 ? PackageConstants.SecureShell.SshForceIPv4 : string.Empty); var startInfo = new ProcessStartInfo(PackageConstants.SecureShell.SshExecutable, arguments) { CreateNoWindow = false, diff --git a/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellKeySetupSettings.cs b/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellKeySetupSettings.cs index 7ccc3313..774eb6d1 100644 --- a/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellKeySetupSettings.cs +++ b/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellKeySetupSettings.cs @@ -1,4 +1,4 @@ -// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- // // Copyright (c) Michael Koster. All rights reserved. // Licensed under the MIT License. @@ -17,6 +17,7 @@ namespace RemoteDebuggerLauncher.RemoteOperations internal class SecureShellKeySetupSettings { private readonly bool forceIPv4; + public SecureShellKeySetupSettings(SetupSshViewModel viewModel) { HostName = viewModel.HostName; @@ -67,5 +68,10 @@ public SecureShellKeySetupSettings(SetupSshViewModel viewModel) /// Gets the private key file. /// public string PrivateKeyFile { get; } + + /// + /// Gets a value indicating whether to force IPv4 usage. + /// + public bool ForceIPv4 => forceIPv4; } } diff --git a/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellPassphraseDialog.xaml b/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellPassphraseDialog.xaml deleted file mode 100644 index 861164ab..00000000 --- a/src/Extension/RemoteDebuggerLauncher/RemoteOperations/SecureShellPassphraseDialog.xaml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 055a709cbb05fc3ede0616a083a1b45d7cb73127 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 23:57:25 +0100 Subject: [PATCH 2/2] Document intentional spacing in SSH format strings (#96) * Initial plan * Add clarifying comments for SSH format string spacing Co-authored-by: MichaelKoster70 <12211710+MichaelKoster70@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: MichaelKoster70 <12211710+MichaelKoster70@users.noreply.github.com> --- src/Extension/RemoteDebuggerLauncher/PackageConstants.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Extension/RemoteDebuggerLauncher/PackageConstants.cs b/src/Extension/RemoteDebuggerLauncher/PackageConstants.cs index 6023f968..705652e8 100644 --- a/src/Extension/RemoteDebuggerLauncher/PackageConstants.cs +++ b/src/Extension/RemoteDebuggerLauncher/PackageConstants.cs @@ -181,6 +181,7 @@ public static class SecureShell public const string DefaultKnownHostsFileName = "known_hosts"; /// SSH key scanner arguments. + /// No space after {2} because SshForceIPv4 includes a trailing space. public const string KeyScanArguments = "{2}-p {1} {0}"; /// Argument for SSH to force IPv4 usage. @@ -190,6 +191,7 @@ public static class SecureShell public const string SshExecutable = "ssh.exe"; /// The SSH arguments to add server fingerprint to known_hosts file. + /// No space after {4} because SshForceIPv4 includes a trailing space. public const string SshArguments = "{0}@{1} {4}-p {2} -i \"{3}\" \"echo DONE\""; /// HTTPS Developer Certificate name.