Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 100 additions & 1 deletion Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions Source/NETworkManager.Localization/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@
<data name="NetworkInterface" xml:space="preserve">
<value>Network Interface</value>
</data>
<data name="NetworkInterfaceConfigurationAppliedSuccessfully" xml:space="preserve">
<value>Configuration applied</value>
</data>
<data name="NetworkInterfaceConfigureAdminMessage" xml:space="preserve">
<value>Configuring the network interface requires elevated rights!</value>
</data>
<data name="NewTab" xml:space="preserve">
<value>New tab</value>
</data>
Expand Down Expand Up @@ -513,6 +519,30 @@
<data name="IPv4Address" xml:space="preserve">
<value>IPv4 address</value>
</data>
<data name="IPv4AddressAddedSuccessfully" xml:space="preserve">
<value>IPv4 address added</value>
</data>
<data name="IPv4AddressRemovedSuccessfully" xml:space="preserve">
<value>IPv4 address removed</value>
</data>
<data name="IPv4AddressReleasedAndRenewedSuccessfully" xml:space="preserve">
<value>IPv4 address released and renewed</value>
</data>
<data name="IPv4AddressReleasedSuccessfully" xml:space="preserve">
<value>IPv4 address released</value>
</data>
<data name="IPv4AddressRenewedSuccessfully" xml:space="preserve">
<value>IPv4 address renewed</value>
</data>
<data name="IPv6AddressReleasedAndRenewedSuccessfully" xml:space="preserve">
<value>IPv6 address released and renewed</value>
</data>
<data name="IPv6AddressReleasedSuccessfully" xml:space="preserve">
<value>IPv6 address released</value>
</data>
<data name="IPv6AddressRenewedSuccessfully" xml:space="preserve">
<value>IPv6 address renewed</value>
</data>
<data name="IPv4DefaultGateway" xml:space="preserve">
<value>IPv4-Default-Gateway</value>
</data>
Expand Down Expand Up @@ -1294,6 +1324,9 @@ Profile files are not affected!</value>
<data name="FlushDNSCache" xml:space="preserve">
<value>Flush DNS cache</value>
</data>
<data name="FlushDNSCacheSuccessfully" xml:space="preserve">
<value>DNS cache flushed</value>
</data>
<data name="HelpMessage_Credentials" xml:space="preserve">
<value>Decrypt and load your credentials to select them.</value>
</data>
Expand Down
128 changes: 59 additions & 69 deletions Source/NETworkManager.Models/Network/NetworkInterface.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
Expand Down Expand Up @@ -407,51 +406,40 @@ private static IPAddress DetectGatewayFromLocalIPAddress(IPAddress localIPAddres
/// </summary>
/// <param name="config">The configuration settings to apply to the network interface. Cannot be null.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public Task ConfigureNetworkInterfaceAsync(NetworkInterfaceConfig config)
public static Task ConfigureNetworkInterfaceAsync(NetworkInterfaceConfig config)
{
return Task.Run(() => ConfigureNetworkInterface(config));
}

/// <summary>
/// Configures the network interface according to the specified settings.
/// </summary>
/// <remarks>This method applies the provided network configuration by executing system commands. If
/// static IP or DNS settings are enabled in the configuration, the corresponding values are set; otherwise, DHCP is
/// used. The method may prompt for elevated permissions depending on system policy.</remarks>
/// <remarks>Requires the application to be running with administrator privileges.</remarks>
/// <param name="config">An object containing the configuration parameters for the network interface, including IP address, subnet mask,
/// gateway, and DNS server settings. Cannot be null.</param>
private void ConfigureNetworkInterface(NetworkInterfaceConfig config)
private static void ConfigureNetworkInterface(NetworkInterfaceConfig config)
{
var name = PowerShellHelper.EscapeSingleQuotes(config.Name);
var commands = new List<string>();

// IP
var command = $"netsh interface ipv4 set address name='{config.Name}'";
command += config.EnableStaticIPAddress
? $" source=static address={config.IPAddress} mask={config.Subnetmask} gateway={config.Gateway};"
: " source=dhcp;";
commands.Add(config.EnableStaticIPAddress
? $"& netsh interface ipv4 set address name='{name}' source=static address={config.IPAddress} mask={config.Subnetmask} gateway={config.Gateway}"
: $"& netsh interface ipv4 set address name='{name}' source=dhcp");

// DNS
command += $"netsh interface ipv4 set DNSservers name='{config.Name}'";
command += config.EnableStaticDNS
? $" source=static address={config.PrimaryDNSServer} register=primary validate=no;"
: " source=dhcp;";
command += config.EnableStaticDNS && !string.IsNullOrEmpty(config.SecondaryDNSServer)
? $"netsh interface ipv4 add DNSservers name='{config.Name}' address={config.SecondaryDNSServer} index=2 validate=no;"
: "";

try
if (config.EnableStaticDNS)
{
PowerShellHelper.ExecuteCommand(command, true);
commands.Add($"& netsh interface ipv4 set dnsservers name='{name}' source=static address={config.PrimaryDNSServer} register=primary validate=no");
if (!string.IsNullOrEmpty(config.SecondaryDNSServer))
commands.Add($"& netsh interface ipv4 add dnsservers name='{name}' address={config.SecondaryDNSServer} index=2 validate=no");
}
catch (Win32Exception win32Ex)
else
{
switch (win32Ex.NativeErrorCode)
{
case 1223:
OnUserHasCanceled();
break;
default:
throw;
}
commands.Add($"& netsh interface ipv4 set dnsservers name='{name}' source=dhcp");
}

RunCommands(commands);
}

/// <summary>
Expand All @@ -466,15 +454,12 @@ public static Task FlushDnsAsync()
}

/// <summary>
/// Clears the local DNS resolver cache on the system by executing the appropriate system command.
/// Clears the local DNS resolver cache on the system.
/// </summary>
/// <remarks>This method requires administrative privileges to successfully flush the DNS cache. If the
/// application does not have sufficient permissions, the operation may fail.</remarks>
/// <remarks>Requires the application to be running with administrator privileges.</remarks>
private static void FlushDns()
{
const string command = "ipconfig /flushdns;";

PowerShellHelper.ExecuteCommand(command);
RunCommands(["& ipconfig /flushdns"], checkExitCode: true);
}

/// <summary>
Expand All @@ -491,29 +476,27 @@ public static Task ReleaseRenewAsync(IPConfigReleaseRenewMode mode, string adapt
/// <summary>
/// Releases and/or renews the IP configuration for the specified network adapter using the given mode.
/// </summary>
/// <remarks>This method executes the appropriate 'ipconfig' commands based on the specified mode. The
/// operation affects only the adapter identified by the provided name. Ensure that the caller has sufficient
/// privileges to modify network settings.</remarks>
/// <param name="mode">A value that specifies which IP configuration operation to perform. Determines whether to release, renew, or
/// perform both actions for IPv4 and/or IPv6 addresses.</param>
/// <param name="adapterName">The name of the network adapter to target for the release or renew operation. Cannot be null or empty.</param>
/// <remarks>Requires the application to be running with administrator privileges.</remarks>
/// <param name="mode">A value that specifies which IP configuration operation to perform.</param>
/// <param name="adapterName">The name of the network adapter to target. Cannot be null or empty.</param>
private static void ReleaseRenew(IPConfigReleaseRenewMode mode, string adapterName)
{
var command = string.Empty;
var name = PowerShellHelper.EscapeSingleQuotes(adapterName);
var lines = new List<string>();

if (mode is IPConfigReleaseRenewMode.ReleaseRenew or IPConfigReleaseRenewMode.Release)
command += $"ipconfig /release '{adapterName}';";
lines.Add($"& ipconfig /release '{name}'");

if (mode is IPConfigReleaseRenewMode.ReleaseRenew or IPConfigReleaseRenewMode.Renew)
command += $"ipconfig /renew '{adapterName}';";
lines.Add($"& ipconfig /renew '{name}'");

if (mode is IPConfigReleaseRenewMode.ReleaseRenew6 or IPConfigReleaseRenewMode.Release6)
command += $"ipconfig /release6 '{adapterName}';";
lines.Add($"& ipconfig /release6 '{name}'");

if (mode is IPConfigReleaseRenewMode.ReleaseRenew6 or IPConfigReleaseRenewMode.Renew6)
command += $"ipconfig /renew6 '{adapterName}';";
lines.Add($"& ipconfig /renew6 '{name}'");

PowerShellHelper.ExecuteCommand(command);
RunCommands(lines, checkExitCode: true);
}

/// <summary>
Expand All @@ -529,21 +512,20 @@ public static Task AddIPAddressToNetworkInterfaceAsync(NetworkInterfaceConfig co
/// <summary>
/// Adds an IP address to the specified network interface using the provided configuration.
/// </summary>
/// <remarks>If DHCP/static IP coexistence is enabled in the configuration, the method enables this
/// feature before adding the IP address. This method requires appropriate system permissions to modify network
/// interface settings.</remarks>
/// <remarks>Requires the application to be running with administrator privileges.</remarks>
/// <param name="config">The network interface configuration containing the interface name, IP address, subnet mask, and DHCP/static
/// coexistence settings. Cannot be null.</param>
private static void AddIPAddressToNetworkInterface(NetworkInterfaceConfig config)
{
var command = string.Empty;
var name = PowerShellHelper.EscapeSingleQuotes(config.Name);
var commands = new List<string>();

if (config.EnableDhcpStaticIpCoexistence)
command += $"netsh interface ipv4 set interface interface='{config.Name}' dhcpstaticipcoexistence=enabled;";
commands.Add($"& netsh interface ipv4 set interface interface='{name}' dhcpstaticipcoexistence=enabled");

command += $"netsh interface ipv4 add address '{config.Name}' {config.IPAddress} {config.Subnetmask};";
commands.Add($"& netsh interface ipv4 add address '{name}' {config.IPAddress} {config.Subnetmask}");

PowerShellHelper.ExecuteCommand(command, true);
RunCommands(commands);
}

/// <summary>
Expand All @@ -559,30 +541,38 @@ public static Task RemoveIPAddressFromNetworkInterfaceAsync(NetworkInterfaceConf
/// <summary>
/// Removes the specified IP address from the given network interface configuration.
/// </summary>
/// <remarks>This method removes the IP address from the network interface using a system command. The
/// operation requires appropriate system permissions and may fail if the interface or IP address does not
/// exist.</remarks>
/// <remarks>Requires the application to be running with administrator privileges.</remarks>
/// <param name="config">The network interface configuration containing the name of the interface and the IP address to remove. Cannot be
/// null.</param>
private static void RemoveIPAddressFromNetworkInterface(NetworkInterfaceConfig config)
{
var command = $"netsh interface ipv4 delete address '{config.Name}' {config.IPAddress};";

PowerShellHelper.ExecuteCommand(command, true);
var name = PowerShellHelper.EscapeSingleQuotes(config.Name);
RunCommands([$"& netsh interface ipv4 delete address '{name}' {config.IPAddress}"]);
}

#endregion
/// <summary>
/// Runs one or more native commands via a PowerShell runspace.
/// When <paramref name="checkExitCode"/> is <see langword="true"/>, a non-zero
/// <c>$LASTEXITCODE</c> is treated as an error via <c>Write-Error</c>.
/// Use <see langword="false"/> for <c>netsh</c>, whose exit codes are unreliable
/// for idempotent operations (e.g. returns 1 when already on DHCP).
/// </summary>
private static void RunCommands(IEnumerable<string> commands, bool checkExitCode = false)
{
string script;

#region Events
if (checkExitCode)
script = string.Join(Environment.NewLine, commands.Select(cmd =>
$"{cmd}{Environment.NewLine}if ($LASTEXITCODE -ne 0) {{ Write-Error \"Command failed with exit code $LASTEXITCODE\" }}"));
else
script = string.Join(Environment.NewLine, commands);

/// <summary>
/// Occurs when the user cancels the current operation (e.g. UAC prompt).
/// </summary>
public event EventHandler UserHasCanceled;
using var ps = SMA.PowerShell.Create();
ps.AddScript(script);
ps.Invoke();

private void OnUserHasCanceled()
{
UserHasCanceled?.Invoke(this, EventArgs.Empty);
if (checkExitCode && ps.HadErrors)
throw new Exception(string.Join("; ", ps.Streams.Error.Select(e => e.ToString())));
Comment on lines +572 to +575
}

#endregion
Expand Down
Loading
Loading