diff --git a/src/SshNetTests/ConnectivityTests.cs b/src/SshNetTests/ConnectivityTests.cs index a43cf85..bdb2e67 100644 --- a/src/SshNetTests/ConnectivityTests.cs +++ b/src/SshNetTests/ConnectivityTests.cs @@ -2,13 +2,9 @@ using Renci.SshNet; using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; +using SshNet.TestTools.OpenSSH; using SshNetTests.Common; using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Management; -using System.Text.RegularExpressions; using System.Threading; namespace SshNetTests @@ -16,8 +12,7 @@ namespace SshNetTests [TestClass] public class ConnectivityTests : TestBase { - private const string NetworkConnectionId = "Ethernet 2"; - + private readonly NetworkConnectivityDisruptor _networkConnectivityDisruptor = NetworkConnectivityDisruptor.Create(); private AuthenticationMethodFactory _authenticationMethodFactory; private IConnectionInfoFactory _connectionInfoFactory; private IConnectionInfoFactory _adminConnectionInfoFactory; @@ -68,203 +63,233 @@ public void Common_CreateMoreChannelsThanMaxSessions() [TestMethod] public void Common_DisposeAfterLossOfNetworkConnectivity() { - var hostNetworkConnectionDisabled = false; - try { - Exception errorOccurred = null; + var hostNetworkConnectionDisabled = false; - using (var client = new SftpClient(_connectionInfoFactory.Create())) + try { - client.ErrorOccurred += (sender, args) => errorOccurred = args.Exception; - client.Connect(); + Exception errorOccurred = null; - DisableHostNetworkConnection(NetworkConnectionId); - hostNetworkConnectionDisabled = true; - } + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.ErrorOccurred += (sender, args) => errorOccurred = args.Exception; + client.Connect(); - Assert.IsNotNull(errorOccurred); - Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + _networkConnectivityDisruptor.Start(); + hostNetworkConnectionDisabled = true; + } - var connectionException = (SshConnectionException) errorOccurred; - Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); - Assert.IsNull(connectionException.InnerException); - Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); - } - finally - { - if (hostNetworkConnectionDisabled) + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + + var connectionException = (SshConnectionException)errorOccurred; + Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); + Assert.IsNull(connectionException.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); + } + finally { - EnableHostNetworkConnection(NetworkConnectionId); - ResetVirtualMachineNetworkConnection(); + if (hostNetworkConnectionDisabled) + { + _networkConnectivityDisruptor.End(); + } } } + catch (NetworkConnectivityDisruptorException ex) + { + Assert.Inconclusive(ex.Message); + } } [TestMethod] public void Common_DetectLossOfNetworkConnectivityThroughKeepAlive() { - using (var client = new SftpClient(_connectionInfoFactory.Create())) + try { - Exception errorOccurred = null; - client.ErrorOccurred += (sender, args) => errorOccurred = args.Exception; - client.KeepAliveInterval = new TimeSpan(0, 0, 0, 0, 50); - client.Connect(); + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + Exception errorOccurred = null; + client.ErrorOccurred += (sender, args) => errorOccurred = args.Exception; + client.KeepAliveInterval = new TimeSpan(0, 0, 0, 0, 50); + client.Connect(); - DisableHostNetworkConnection(NetworkConnectionId); + _networkConnectivityDisruptor.Start(); - try - { - for (var i = 0; i < 500; i++) + try { - if (!client.IsConnected) - break; - Thread.Sleep(100); - } - Assert.IsFalse(client.IsConnected); + for (var i = 0; i < 500; i++) + { + if (!client.IsConnected) + break; + Thread.Sleep(100); + } - Assert.IsNotNull(errorOccurred); - Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + Assert.IsFalse(client.IsConnected); - var connectionException = (SshConnectionException)errorOccurred; - Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); - Assert.IsNull(connectionException.InnerException); - Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); - } - finally - { - EnableHostNetworkConnection(NetworkConnectionId); - ResetVirtualMachineNetworkConnection(); + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + + var connectionException = (SshConnectionException)errorOccurred; + Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); + Assert.IsNull(connectionException.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); + } + finally + { + _networkConnectivityDisruptor.End(); + } } } + catch (NetworkConnectivityDisruptorException ex) + { + Assert.Inconclusive(ex.Message); + } } [TestMethod] public void Common_DetectConnectionResetThroughSftpInvocation() { - using (var client = new SftpClient(_connectionInfoFactory.Create())) + try { - ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false); - Exception errorOccurred = null; - client.ErrorOccurred += (sender, args) => - { - errorOccurred = args.Exception; - errorOccurredSignaled.Set(); - }; - client.Connect(); + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + client.KeepAliveInterval = TimeSpan.FromSeconds(1); + client.OperationTimeout = TimeSpan.FromSeconds(60); + ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false); + Exception errorOccurred = null; + client.ErrorOccurred += (sender, args) => + { + errorOccurred = args.Exception; + errorOccurredSignaled.Set(); + }; + client.Connect(); - DisableHostNetworkConnection(NetworkConnectionId); + _networkConnectivityDisruptor.Start(); - try - { - client.ListDirectory("/"); - Assert.Fail(); - } - catch (SshConnectionException ex) - { - Assert.IsNull(ex.InnerException); - Assert.AreEqual("Client not connected.", ex.Message); + try + { + client.ListDirectory("/"); + Assert.Fail(); + } + catch (SshConnectionException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Client not connected.", ex.Message); - Assert.IsNotNull(errorOccurred); - Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); + Assert.IsNotNull(errorOccurred); + Assert.AreEqual(typeof(SshConnectionException), errorOccurred.GetType()); - var connectionException = (SshConnectionException) errorOccurred; - Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); - Assert.IsNull(connectionException.InnerException); - Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); - } - finally - { - EnableHostNetworkConnection(NetworkConnectionId); - ResetVirtualMachineNetworkConnection(); + var connectionException = (SshConnectionException)errorOccurred; + Assert.AreEqual(DisconnectReason.ConnectionLost, connectionException.DisconnectReason); + Assert.IsNull(connectionException.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", connectionException.Message); + } + finally + { + _networkConnectivityDisruptor.End(); + } } } + catch (NetworkConnectivityDisruptorException ex) + { + Assert.Inconclusive(ex.Message); + } } [TestMethod] public void Common_LossOfNetworkConnectivityDisconnectAndConnect() { - bool vmNetworkConnectionDisabled = false; - try { - using (var client = new SftpClient(_connectionInfoFactory.Create())) + bool vmNetworkConnectionDisabled = false; + + try { - Exception errorOccurred = null; - client.ErrorOccurred += (sender, args) => errorOccurred = args.Exception; + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + Exception errorOccurred = null; + client.ErrorOccurred += (sender, args) => errorOccurred = args.Exception; - client.Connect(); + client.Connect(); - DisableVirtualMachineNetworkConnection(); - vmNetworkConnectionDisabled = true; - ResetVirtualMachineNetworkConnection(); + _networkConnectivityDisruptor.Start(); + vmNetworkConnectionDisabled = true; - // disconnect while network connectivity is lost - client.Disconnect(); + // disconnect while network connectivity is lost + client.Disconnect(); - Assert.IsFalse(client.IsConnected); + Assert.IsFalse(client.IsConnected); - EnableVirtualMachineNetworkConnection(); - vmNetworkConnectionDisabled = false; - ResetVirtualMachineNetworkConnection(); + _networkConnectivityDisruptor.End(); + vmNetworkConnectionDisabled = false; - // connect when network connectivity is restored - client.Connect(); - client.ChangeDirectory(client.WorkingDirectory); - client.Dispose(); + // connect when network connectivity is restored + client.Connect(); + client.ChangeDirectory(client.WorkingDirectory); + client.Dispose(); - Assert.IsNull(errorOccurred); + Assert.IsNull(errorOccurred); + } } - } - finally - { - if (vmNetworkConnectionDisabled) + finally { - EnableVirtualMachineNetworkConnection(); - ResetVirtualMachineNetworkConnection(); + if (vmNetworkConnectionDisabled) + { + _networkConnectivityDisruptor.End(); + } } } + catch (NetworkConnectivityDisruptorException ex) + { + Assert.Inconclusive(ex.Message); + } } [TestMethod] public void Common_DetectLossOfNetworkConnectivityThroughSftpInvocation() { - using (var client = new SftpClient(_connectionInfoFactory.Create())) + try { - ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false); - Exception errorOccurred = null; - client.ErrorOccurred += (sender, args) => - { - errorOccurred = args.Exception; - errorOccurredSignaled.Set(); - }; - client.Connect(); + using (var client = new SftpClient(_connectionInfoFactory.Create())) + { + ManualResetEvent errorOccurredSignaled = new ManualResetEvent(false); + Exception errorOccurred = null; + client.ErrorOccurred += (sender, args) => + { + errorOccurred = args.Exception; + errorOccurredSignaled.Set(); + }; + client.Connect(); - DisableVirtualMachineNetworkConnection(); - ResetVirtualMachineNetworkConnection(); + _networkConnectivityDisruptor.Start(); - try - { - client.ListDirectory("/"); - Assert.Fail(); - } - catch (SshConnectionException ex) - { - Assert.AreEqual(DisconnectReason.ConnectionLost, ex.DisconnectReason); - Assert.IsNull(ex.InnerException); - Assert.AreEqual("An established connection was aborted by the server.", ex.Message); - } - finally - { - EnableVirtualMachineNetworkConnection(); - ResetVirtualMachineNetworkConnection(); + try + { + client.ListDirectory("/"); + Assert.Fail(); + } + catch (SshConnectionException ex) + { + Assert.AreEqual(DisconnectReason.ConnectionLost, ex.DisconnectReason); + Assert.IsNull(ex.InnerException); + Assert.AreEqual("An established connection was aborted by the server.", ex.Message); + } + finally + { + _networkConnectivityDisruptor.End(); + } } } + catch (NetworkConnectivityDisruptorException ex) + { + Assert.Inconclusive(ex.Message); + } } [TestMethod] - public void Common_DetectSessionKilledOnServer() + public void Common_DetectSessionKilledOnServer() { using (var client = new SftpClient(_connectionInfoFactory.Create())) { @@ -387,205 +412,7 @@ public void Common_ServerRejectsConnection() } } - private static void DisableHostNetworkConnection(string networkConnection) - { - SelectQuery wmiQuery = new SelectQuery("SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionId != NULL"); - ManagementObjectSearcher searchProcedure = new ManagementObjectSearcher(wmiQuery); - foreach (ManagementObject item in searchProcedure.Get()) - { - var netConnectionId = (string)item["NetConnectionId"]; - - if (netConnectionId == networkConnection) - { - var returnValue = item.InvokeMethod("Disable", null); - if (returnValue is uint retValue) - { - if (retValue == 0) - { - return; - } - - throw new ApplicationException($"Failed to disable '{networkConnection}' network connection. Return value is {retValue}.{Environment.NewLine}Make sure you're running the tests with elevated priviliges."); - } - else if (returnValue == null) - { - throw new ApplicationException($"Failed to disable '{networkConnection}' network connection. Return value is null."); - } - else - { - throw new ApplicationException($"Failed to disable '{networkConnection}' network connection. Unexpected return value {returnValue} ({returnValue.GetType()})."); - } - } - } - - throw new ApplicationException($"Failed to disable '{networkConnection}' network connection. Network connection not found."); - } - - private static void EnableHostNetworkConnection(string networkConnection) - { - SelectQuery wmiQuery = new SelectQuery("SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionId != NULL"); - ManagementObjectSearcher searchProcedure = new ManagementObjectSearcher(wmiQuery); - foreach (ManagementObject item in searchProcedure.Get()) - { - var netConnectionId = (string)item["NetConnectionId"]; - - if (netConnectionId == networkConnection) - { - var returnValue = item.InvokeMethod("Enable", null); - if (returnValue is uint retValue) - { - if (retValue == 0u) - { - Console.WriteLine($"Enable host network connection for '{networkConnection}'."); - Thread.Sleep(5000); - return; - } - throw new ApplicationException($"Failed to enable '{networkConnection}' network connection. Return value is {retValue}..{Environment.NewLine}Make sure you're running the tests with elevated priviliges."); - } - else if (returnValue == null) - { - throw new ApplicationException($"Failed to enable '{networkConnection}' network connection. Return value is null."); - } - else - { - throw new ApplicationException($"Failed to enable '{networkConnection}' network connection. Unexpected return value {returnValue} ({returnValue.GetType()})."); - } - } - } - throw new ApplicationException($"Failed to enable '{networkConnection}' network connection. Network connection not found."); - } - - private static string VirtualBoxFolder - { - get - { - if (Environment.Is64BitOperatingSystem) - { - if (!Environment.Is64BitProcess) - { - // dotnet test runs tests in a 32-bit process (no watter what I f***in' try), so let's hard-code the - // path to VirtualBox - return Path.Combine("c:\\Program Files", "Oracle", "VirtualBox"); - } - } - - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Oracle", "VirtualBox"); - } - } - - private static List GetRunningVMs() - { - var runningVmRegex = new Regex("\"(?.+?)\"\\s?(?{.+?})"); - - var startInfo = new ProcessStartInfo - { - FileName = Path.Combine(VirtualBoxFolder, "VBoxManage.exe"), - Arguments = "list runningvms", - RedirectStandardOutput = true, - UseShellExecute = false - }; - - var process = Process.Start(startInfo); - process.WaitForExit(); - - if (process.ExitCode != 0) - { - throw new ApplicationException($"Failed to get list of running VMs. Exit code is {process.ExitCode}."); - } - - var runningVms = new List(); - - string line; - - while ((line = process.StandardOutput.ReadLine()) != null) - { - var match = runningVmRegex.Match(line); - if (match != null) - { - runningVms.Add(match.Groups["name"].Value); - } - } - - return runningVms; - } - - private static void SetLinkState(string vmName, bool on) - { - var linkStateValue = (on ? "on" : "off"); - - var startInfo = new ProcessStartInfo - { - FileName = Path.Combine(VirtualBoxFolder, "VBoxManage.exe"), - Arguments = $"controlvm \"{vmName}\" setlinkstate1 {linkStateValue}", - RedirectStandardOutput = true, - UseShellExecute = false - }; - - var process = Process.Start(startInfo); - process.WaitForExit(); - - if (process.ExitCode != 0) - { - throw new ApplicationException($"Failed to set linkstate for VM '{vmName}' to '{linkStateValue}'. Exit code is {process.ExitCode}."); - } - else - { - Console.WriteLine($"Changed linkstate for VM '{vmName}' to '{linkStateValue}."); - } - } - - private static void SetPromiscuousMode(string vmName, string value) - { - var startInfo = new ProcessStartInfo - { - FileName = Path.Combine(VirtualBoxFolder, "VBoxManage.exe"), - Arguments = $"controlvm \"{vmName}\" nicpromisc1 {value}", - RedirectStandardOutput = true, - UseShellExecute = false - }; - - var process = Process.Start(startInfo); - process.WaitForExit(); - - if (process.ExitCode != 0) - { - throw new ApplicationException($"Failed to set promiscuous for VM '{vmName}' to '{value}'. Exit code is {process.ExitCode}."); - } - else - { - Console.WriteLine($"Changed promiscuous for VM '{vmName}' to '{value}'."); - } - } - - private static void DisableVirtualMachineNetworkConnection() - { - var runningVMs = GetRunningVMs(); - Assert.AreEqual(1, runningVMs.Count); - - SetLinkState(runningVMs[0], false); - Thread.Sleep(1000); - } - - private static void EnableVirtualMachineNetworkConnection() - { - var runningVMs = GetRunningVMs(); - Assert.AreEqual(1, runningVMs.Count); - - SetLinkState(runningVMs[0], true); - Thread.Sleep(1000); - } - - private static void ResetVirtualMachineNetworkConnection() - { - var runningVMs = GetRunningVMs(); - Assert.AreEqual(1, runningVMs.Count); - - SetPromiscuousMode(runningVMs[0], "allow-all"); - Thread.Sleep(1000); - SetPromiscuousMode(runningVMs[0], "deny"); - Thread.Sleep(1000); - } } } diff --git a/src/SshNetTests/Temp4Tools/DockerNetworkConnectivityDisruptor.cs b/src/SshNetTests/Temp4Tools/DockerNetworkConnectivityDisruptor.cs new file mode 100644 index 0000000..ab39804 --- /dev/null +++ b/src/SshNetTests/Temp4Tools/DockerNetworkConnectivityDisruptor.cs @@ -0,0 +1,70 @@ +using System; +using System.Diagnostics; + +namespace SshNet.TestTools.OpenSSH +{ + public sealed class DockerNetworkConnectivityDisruptor : NetworkConnectivityDisruptor + { + [Obsolete("This method does not work, but is left here to test some other possibilities...")] + public DockerNetworkConnectivityDisruptor(string containerName) + { + ContainerName = containerName; + } + + private string ContainerName { get; } + + public override void Start() + { + try + { + var process = DockerCommand($"pause {ContainerName}"); + if (!process.WaitForExit(30000)) + { + process.Kill(); + throw new ApplicationException("Timed out."); + } + if (process.ExitCode != 0) + { + throw new ApplicationException($"Status {process.ExitCode}."); + } + } + catch (Exception ex) + { + throw new NetworkConnectivityDisruptorException($"Failed to pause container '{ContainerName}': {ex.Message}", ex); + } + } + + public override void End() + { + try + { + var process = DockerCommand($"unpause {ContainerName}"); + if (!process.WaitForExit(30000)) + { + process.Kill(); + throw new ApplicationException("Timed out."); + } + if (process.ExitCode != 0) + { + throw new ApplicationException($"Status {process.ExitCode}."); + } + } + catch (Exception ex) + { + throw new NetworkConnectivityDisruptorException($"Failed to resume container '{ContainerName}': {ex.Message}", ex); + } + } + + private static Process DockerCommand(string command) + { + return Process.Start( + new ProcessStartInfo + { + FileName = "docker", + Arguments = command, + RedirectStandardOutput = true, + UseShellExecute = false + }); + } + } +} diff --git a/src/SshNetTests/Temp4Tools/NetworkConnectivityDisruptor.cs b/src/SshNetTests/Temp4Tools/NetworkConnectivityDisruptor.cs new file mode 100644 index 0000000..6208955 --- /dev/null +++ b/src/SshNetTests/Temp4Tools/NetworkConnectivityDisruptor.cs @@ -0,0 +1,17 @@ +namespace SshNet.TestTools.OpenSSH +{ + public abstract class NetworkConnectivityDisruptor + { + public abstract void Start(); + + public abstract void End(); + + public static NetworkConnectivityDisruptor Create() + { + // ToDo: Here we decide which environment we are running in... somehow :) + // Maybe environment variables: easy to setup and persist independently of the code? +// return new DockerNetworkConnectivityDisruptor(containerName: "sshnet"); + return new PhysicalNetworkConnectivityDisruptor(connectionName: "Management"); + } + } +} diff --git a/src/SshNetTests/Temp4Tools/NetworkConnectivityDisruptorException.cs b/src/SshNetTests/Temp4Tools/NetworkConnectivityDisruptorException.cs new file mode 100644 index 0000000..2789336 --- /dev/null +++ b/src/SshNetTests/Temp4Tools/NetworkConnectivityDisruptorException.cs @@ -0,0 +1,12 @@ +using System; + +namespace SshNet.TestTools.OpenSSH +{ + public sealed class NetworkConnectivityDisruptorException : Exception + { + public NetworkConnectivityDisruptorException(string message, Exception innerException) : + base(message, innerException) + { + } + } +} diff --git a/src/SshNetTests/Temp4Tools/PhysicalNetworkConnectivityDisruptor.cs b/src/SshNetTests/Temp4Tools/PhysicalNetworkConnectivityDisruptor.cs new file mode 100644 index 0000000..c874b08 --- /dev/null +++ b/src/SshNetTests/Temp4Tools/PhysicalNetworkConnectivityDisruptor.cs @@ -0,0 +1,63 @@ +using System; +using System.ComponentModel; +using System.Management; + +namespace SshNet.TestTools.OpenSSH +{ + public sealed class PhysicalNetworkConnectivityDisruptor : NetworkConnectivityDisruptor + { + public PhysicalNetworkConnectivityDisruptor(string connectionName) + { + ConnectionName = connectionName ?? throw new ArgumentNullException(nameof(connectionName)); + } + + private string ConnectionName { get; } + + + public override void Start() + { + try + { + InvokeMethod(GetNetConnection(ConnectionName), "Disable"); + } + catch (Exception ex) + { + throw new NetworkConnectivityDisruptorException($"Failed to disable network connection '{ConnectionName}': {ex.Message}", ex); + } + } + + public override void End() + { + try + { + InvokeMethod(GetNetConnection(ConnectionName), "Enable"); + } + catch (Exception ex) + { + throw new NetworkConnectivityDisruptorException($"Failed to enable network connection '{ConnectionName}': {ex.Message}", ex); + } + } + + private ManagementObject GetNetConnection(string connectionName) + { + // ToDo: We could probably cache the ManagementObject + SelectQuery wmiQuery = new SelectQuery($"SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionId = '{connectionName}'"); + ManagementObjectSearcher searchProcedure = new ManagementObjectSearcher(wmiQuery); + foreach (ManagementObject item in searchProcedure.Get()) + { + return item; + } + + throw new Exception("The connection does not exist."); + } + + private void InvokeMethod(ManagementObject networkConnection, string methodName) + { + var returnValue = (uint)networkConnection.InvokeMethod(methodName, null); + if (returnValue != 0) + { + throw new Win32Exception(unchecked((int)returnValue)); + } + } + } +} diff --git a/src/SshNetTests/Temp4Tools/VirtualBoxNetworkConnectivityDisruptor.cs b/src/SshNetTests/Temp4Tools/VirtualBoxNetworkConnectivityDisruptor.cs new file mode 100644 index 0000000..783f1ac --- /dev/null +++ b/src/SshNetTests/Temp4Tools/VirtualBoxNetworkConnectivityDisruptor.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading; + +namespace SshNet.TestTools.OpenSSH +{ + public sealed class VirtualBoxNetworkConnectivityDisruptor : NetworkConnectivityDisruptor + { + public VirtualBoxNetworkConnectivityDisruptor(string machineName) + { + MachineName = machineName; + } + + private string MachineName { get; } + + + public override void Start() + { + try + { + SetLinkState(MachineName, on: false); + } + catch (Exception ex) + { + throw new NetworkConnectivityDisruptorException($"Failed to disable link for VM '{MachineName}': {ex.Message}", ex); + } + } + + public override void End() + { + try + { + SetLinkState(MachineName, on: true); + } + catch (Exception ex) + { + throw new NetworkConnectivityDisruptorException($"Failed to enable link for VM '{MachineName}': {ex.Message}", ex); + } + } + + private static string VirtualBoxFolder + { + get + { + if (Environment.Is64BitOperatingSystem) + { + if (!Environment.Is64BitProcess) + { + // dotnet test runs tests in a 32-bit process (no watter what I f***in' try), so let's hard-code the + // path to VirtualBox + return Path.Combine("c:\\Program Files", "Oracle", "VirtualBox"); + } + } + + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Oracle", "VirtualBox"); + } + } + + private static List GetRunningVMs() + { + var runningVmRegex = new Regex("\"(?.+?)\"\\s?(?{.+?})"); + + var startInfo = new ProcessStartInfo + { + FileName = Path.Combine(VirtualBoxFolder, "VBoxManage.exe"), + Arguments = "list runningvms", + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var process = Process.Start(startInfo); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + throw new ApplicationException($"Failed to get list of running VMs. Exit code is {process.ExitCode}."); + } + + var runningVms = new List(); + + string line; + + while ((line = process.StandardOutput.ReadLine()) != null) + { + var match = runningVmRegex.Match(line); + if (match != null) + { + runningVms.Add(match.Groups["name"].Value); + } + } + + return runningVms; + } + + private static void SetLinkState(string vmName, bool on) + { + var linkStateValue = (on ? "on" : "off"); + + var startInfo = new ProcessStartInfo + { + FileName = Path.Combine(VirtualBoxFolder, "VBoxManage.exe"), + Arguments = $"controlvm \"{vmName}\" setlinkstate1 {linkStateValue}", + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var process = Process.Start(startInfo); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + throw new ApplicationException($"Failed to set linkstate for VM '{vmName}' to '{linkStateValue}'. Exit code is {process.ExitCode}."); + } + else + { + Console.WriteLine($"Changed linkstate for VM '{vmName}' to '{linkStateValue}."); + } + } + + private static void SetPromiscuousMode(string vmName, string value) + { + var startInfo = new ProcessStartInfo + { + FileName = Path.Combine(VirtualBoxFolder, "VBoxManage.exe"), + Arguments = $"controlvm \"{vmName}\" nicpromisc1 {value}", + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var process = Process.Start(startInfo); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + throw new ApplicationException($"Failed to set promiscuous for VM '{vmName}' to '{value}'. Exit code is {process.ExitCode}."); + } + else + { + Console.WriteLine($"Changed promiscuous for VM '{vmName}' to '{value}'."); + } + } + + private static void DisableVirtualMachineNetworkConnection() + { + var runningVMs = GetRunningVMs(); + // Assert.AreEqual(1, runningVMs.Count); + + SetLinkState(runningVMs[0], false); + Thread.Sleep(1000); + } + + private static void EnableVirtualMachineNetworkConnection() + { + var runningVMs = GetRunningVMs(); + // Assert.AreEqual(1, runningVMs.Count); + + SetLinkState(runningVMs[0], true); + Thread.Sleep(1000); + } + + private static void ResetVirtualMachineNetworkConnection() + { + var runningVMs = GetRunningVMs(); + // Assert.AreEqual(1, runningVMs.Count); + + SetPromiscuousMode(runningVMs[0], "allow-all"); + Thread.Sleep(1000); + SetPromiscuousMode(runningVMs[0], "deny"); + Thread.Sleep(1000); + } + + } +}