170 lines
5.2 KiB
C#
170 lines
5.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Net.NetworkInformation;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Principal;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace StreamPlayer.Desktop.Services;
|
|
|
|
public sealed class WindowsDnsService
|
|
{
|
|
private static readonly string[] PreferredDns = { "8.8.8.8", "8.8.4.4" };
|
|
private bool _attempted;
|
|
|
|
public async Task<DnsSetupResult> EnsureGoogleDnsAsync(CancellationToken cancellationToken)
|
|
{
|
|
if (!OperatingSystem.IsWindows())
|
|
{
|
|
return DnsSetupResult.CreateSuccess();
|
|
}
|
|
|
|
if (_attempted)
|
|
{
|
|
return DnsSetupResult.CreateSuccess();
|
|
}
|
|
|
|
_attempted = true;
|
|
|
|
bool needsElevation = !IsRunningAsAdministrator();
|
|
if (needsElevation)
|
|
{
|
|
var consent = PromptForElevation();
|
|
if (!consent)
|
|
{
|
|
return DnsSetupResult.CreateFailure("Se canceló la solicitud de permisos. Ejecuta la app como administrador o configura los DNS manualmente (8.8.8.8 y 8.8.4.4).");
|
|
}
|
|
}
|
|
|
|
var interfaces = GetEligibleInterfaces().ToList();
|
|
if (interfaces.Count == 0)
|
|
{
|
|
return DnsSetupResult.CreateSuccess("No se detectaron adaptadores de red activos para forzar DNS.");
|
|
}
|
|
|
|
foreach (var adapter in interfaces)
|
|
{
|
|
bool primary = await RunNetshAsync(
|
|
$"interface ipv4 set dns name=\"{adapter}\" static {PreferredDns[0]} primary",
|
|
cancellationToken,
|
|
needsElevation);
|
|
bool secondary = await RunNetshAsync(
|
|
$"interface ipv4 add dns name=\"{adapter}\" {PreferredDns[1]} index=2",
|
|
cancellationToken,
|
|
needsElevation);
|
|
if (!primary || !secondary)
|
|
{
|
|
return DnsSetupResult.CreateFailure($"No se pudo configurar DNS para el adaptador \"{adapter}\". Verifica permisos de administrador o configura manualmente los DNS de Google.");
|
|
}
|
|
}
|
|
|
|
return DnsSetupResult.CreateSuccess("DNS de Google aplicados correctamente a los adaptadores de red activos.");
|
|
}
|
|
|
|
private static bool IsRunningAsAdministrator()
|
|
{
|
|
if (!OperatingSystem.IsWindows())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
using var identity = WindowsIdentity.GetCurrent();
|
|
var principal = new WindowsPrincipal(identity);
|
|
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<string> GetEligibleInterfaces()
|
|
{
|
|
return NetworkInterface.GetAllNetworkInterfaces()
|
|
.Where(ni =>
|
|
ni.OperationalStatus == OperationalStatus.Up &&
|
|
ni.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
|
|
ni.NetworkInterfaceType != NetworkInterfaceType.Tunnel &&
|
|
ni.Supports(NetworkInterfaceComponent.IPv4))
|
|
.Select(ni => ni.Name);
|
|
}
|
|
|
|
private static bool PromptForElevation()
|
|
{
|
|
try
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = "netsh",
|
|
Arguments = "advfirewall show currentprofile",
|
|
UseShellExecute = true,
|
|
Verb = "runas",
|
|
CreateNoWindow = true
|
|
};
|
|
using var process = Process.Start(psi);
|
|
if (process == null)
|
|
{
|
|
return false;
|
|
}
|
|
process.WaitForExit();
|
|
return process.ExitCode == 0;
|
|
}
|
|
catch (System.ComponentModel.Win32Exception ex) when (ex.NativeErrorCode == 1223)
|
|
{
|
|
// User cancelled UAC prompt.
|
|
return false;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static async Task<bool> RunNetshAsync(string arguments, CancellationToken cancellationToken, bool elevate)
|
|
{
|
|
try
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = "netsh",
|
|
Arguments = arguments,
|
|
UseShellExecute = elevate,
|
|
RedirectStandardOutput = !elevate,
|
|
RedirectStandardError = !elevate,
|
|
CreateNoWindow = true
|
|
};
|
|
if (elevate)
|
|
{
|
|
psi.Verb = "runas";
|
|
}
|
|
|
|
using var process = Process.Start(psi);
|
|
if (process == null)
|
|
{
|
|
return false;
|
|
}
|
|
await process.WaitForExitAsync(cancellationToken);
|
|
return process.ExitCode == 0;
|
|
}
|
|
catch (System.ComponentModel.Win32Exception ex) when (ex.NativeErrorCode == 1223)
|
|
{
|
|
return false;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed record DnsSetupResult(bool Success, string Message)
|
|
{
|
|
public static DnsSetupResult CreateSuccess(string message = "") => new(true, message);
|
|
public static DnsSetupResult CreateFailure(string message) => new(false, message);
|
|
}
|