Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 8 additions & 2 deletions ServerPickerX/ServerPickerX.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<PublishSingleFile>true</PublishSingleFile>
<!-- Unnecessary assemblies or unused code are trimmed to reduce build size, this is very fragile to code changes -->
<PublishTrimmed>true</PublishTrimmed>
<!-- Required for Windows firewall COM interop -->
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<!-- Use partial trim mode else XAML processing (Locale dictionaries will cause errors if property is not set) -->
<!-- Had to search the entire internet for this, AvaloniaUI lacks proper documentations -->
<TrimMode>partial</TrimMode>
Expand All @@ -20,8 +22,8 @@
<PackageProjectUrl>https://github.com/FN-FAL113/server-picker-x</PackageProjectUrl>
<RepositoryUrl>https://github.com/FN-FAL113/server-picker-x</RepositoryUrl>
<ApplicationIcon>Assets\favicon.ico</ApplicationIcon>
<AssemblyVersion>1.0.3.0</AssemblyVersion>
<FileVersion>1.0.3.0</FileVersion>
<AssemblyVersion>1.0.4.0</AssemblyVersion>
<FileVersion>1.0.4.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand All @@ -31,6 +33,10 @@
</ItemGroup>

<ItemGroup>
<Reference Include="Interop.NetFwTypeLib">
<HintPath>libs\Interop.NetFwTypeLib.dll</HintPath>
<EmbedInteropTypes>true</EmbedInteropTypes>
</Reference>
<PackageReference Include="Avalonia" Version="11.3.12" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.12" />
Expand Down
130 changes: 73 additions & 57 deletions ServerPickerX/Services/SystemFirewalls/WindowsFirewallService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using NetFwTypeLib;

namespace ServerPickerX.Services.SystemFirewalls
{
Expand All @@ -20,73 +21,64 @@
IProcessService _processService
) : ISystemFirewallService
{
private const string _firewallRulePrefix = "server_picker_x_";
private const NET_FW_RULE_DIRECTION_ _firewallRuleDirectionOutbound = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_OUT;
private const NET_FW_ACTION_ _firewallRuleActionBlock = NET_FW_ACTION_.NET_FW_ACTION_BLOCK;
private const int _firewallRuleProtocolAny = 256;
private const int _firewallRuleProfilesAll = int.MaxValue;

public async Task BlockServersAsync(ObservableCollection<ServerModel> serverModels)
{
foreach (var serverModel in serverModels)
try
{
string ipAddresses = string.Join(",", serverModel.RelayModels.Select(s => s.IPv4).ToList());

using var process = _processService.CreateProcess("cmd.exe");

process.StartInfo.Arguments = $"/c {Path.Combine(Environment.SystemDirectory, "netsh.exe")} " +
"advfirewall firewall " +
"add rule " +
"name=server_picker_x_" + serverModel.Description.Replace(" ", "") +
" dir=out action=block protocol=ANY " + "remoteip=" + ipAddresses;

try
{
process.Start();
await process.WaitForExitAsync();

string stdOut = process.StandardOutput.ReadToEnd().Trim();
string stdErr = process.StandardError.ReadToEnd().Trim();
INetFwPolicy2 firewallPolicy = GetFirewallPolicyApi();
INetFwRules firewallRules = firewallPolicy.Rules;

if (process.ExitCode > 0)
{
await _loggerService.LogWarningAsync("StdOut: " + stdOut + " StdErr: " + stdErr);
}
}
catch (Exception ex)
foreach (var serverModel in serverModels)
{
// Perform debugging here if necessary (log error or through debugger breakpoints)
throw;
string ruleName = GetFirewallRuleName(serverModel);
string ipAddresses = string.Join(",", serverModel.RelayModels.Select(s => s.IPv4));

RemoveFirewallRuleByName(firewallRules, ruleName);

INetFwRule firewallRule = CreateFirewallRuleApi();
firewallRule.Name = ruleName;
firewallRule.Description = serverModel.Description;
firewallRule.Direction = _firewallRuleDirectionOutbound;
firewallRule.Action = _firewallRuleActionBlock;
firewallRule.Protocol = _firewallRuleProtocolAny;
firewallRule.RemoteAddresses = ipAddresses;
firewallRule.Enabled = true;
firewallRule.Profiles = _firewallRuleProfilesAll;

firewallRules.Add(firewallRule);
}
}
catch (Exception ex)
{
await _loggerService.LogErrorAsync(ex.Message);
throw;
}
}

public async Task UnblockServersAsync(ObservableCollection<ServerModel> serverModels)
{
foreach (var serverModel in serverModels)
try
{
string ipAddresses = string.Join(",", serverModel.RelayModels.Select(s => s.IPv4).ToList());

using var process = _processService.CreateProcess("cmd.exe");

process.StartInfo.Arguments = $"/c {Path.Combine(Environment.SystemDirectory, "netsh.exe")} " +
"advfirewall firewall " +
"delete rule " +
"name=server_picker_x_" + serverModel.Description.Replace(" ", "");
INetFwPolicy2 firewallPolicy = GetFirewallPolicyApi();
INetFwRules firewallRules = firewallPolicy.Rules;

try
{
process.Start();
await process.WaitForExitAsync();

string stdOut = process.StandardOutput.ReadToEnd().Trim();
string stdErr = process.StandardError.ReadToEnd().Trim();

if (process.ExitCode > 0)
{
await _loggerService.LogWarningAsync("StdOut: " + stdOut + " StdErr: " + stdErr);
}
}
catch (Exception ex)
foreach (var serverModel in serverModels)
{
// Perform debugging here if necessary (log error or through debugger breakpoints)
throw;
string ruleName = GetFirewallRuleName(serverModel);
RemoveFirewallRuleByName(firewallRules, ruleName);
}
}
catch (Exception ex)
{
await _loggerService.LogErrorAsync(ex.Message);
throw;
}
}

public async Task ResetFirewallAsync()
Expand All @@ -97,10 +89,7 @@
MsBox.Avalonia.Enums.Icon.Warning
);

if (!result)
{
return;
}
if (!result) return;

using Process process = _processService.CreateProcess("cmd.exe");

Expand All @@ -127,9 +116,36 @@
}
catch (Exception ex)
{
// Perform debugging here if necessary (log error or through debugger breakpoints)
await _loggerService.LogErrorAsync(ex.Message);
throw;
}
}

public INetFwPolicy2 GetFirewallPolicyApi()
=> (INetFwPolicy2)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("E2B3C97F-6AE1-41AC-817A-F6F92166D7DD"))!)!;

Check warning on line 125 in ServerPickerX/Services/SystemFirewalls/WindowsFirewallService.cs

View workflow job for this annotation

GitHub Actions / build

'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Activator.CreateInstance(Type)'. The return value of method 'System.Type.GetTypeFromCLSID(Guid)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.

Check warning on line 125 in ServerPickerX/Services/SystemFirewalls/WindowsFirewallService.cs

View workflow job for this annotation

GitHub Actions / build

This call site is reachable on all platforms. 'Type.GetTypeFromCLSID(Guid)' is only supported on: 'windows'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

public INetFwRule CreateFirewallRuleApi()
=> (INetFwRule)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("2C5BC43E-3369-4C33-AB0C-BE9469677AF4"))!)!;

Check warning on line 128 in ServerPickerX/Services/SystemFirewalls/WindowsFirewallService.cs

View workflow job for this annotation

GitHub Actions / build

'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Activator.CreateInstance(Type)'. The return value of method 'System.Type.GetTypeFromCLSID(Guid)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.

Check warning on line 128 in ServerPickerX/Services/SystemFirewalls/WindowsFirewallService.cs

View workflow job for this annotation

GitHub Actions / build

This call site is reachable on all platforms. 'Type.GetTypeFromCLSID(Guid)' is only supported on: 'windows'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

public string GetFirewallRuleName(ServerModel serverModel)
=> _firewallRulePrefix + serverModel.Description.Replace(" ", "");

public void RemoveFirewallRuleByName(INetFwRules firewallRules, string ruleName)
{
while (TryGetFirewallRule(firewallRules, ruleName) != null)
{
firewallRules.Remove(ruleName);
}
}

public INetFwRule? TryGetFirewallRule(INetFwRules firewallRules, string ruleName)
{
try
{
return firewallRules.Item(ruleName);
} catch {
return null;
}
}
}
}
}
Binary file added ServerPickerX/libs/Interop.NetFwTypeLib.dll
Binary file not shown.
Loading