向系统插入自身做为主DNS

This commit is contained in:
xljiulang 2021-07-20 20:25:14 +08:00
parent 9451152d76
commit 4ae101a76f
2 changed files with 115 additions and 99 deletions

View File

@ -6,8 +6,6 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -19,50 +17,37 @@ namespace FastGithub.Dns
sealed class DnsServerHostedService : BackgroundService sealed class DnsServerHostedService : BackgroundService
{ {
private readonly RequestResolver requestResolver; private readonly RequestResolver requestResolver;
private readonly FastGithubConfig fastGithubConfig;
private readonly HostsValidator hostsValidator; private readonly HostsValidator hostsValidator;
private readonly ILogger<DnsServerHostedService> logger; private readonly ILogger<DnsServerHostedService> logger;
private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
private readonly byte[] buffer = new byte[ushort.MaxValue]; private readonly byte[] buffer = new byte[ushort.MaxValue];
private IPAddress[]? dnsAddresses;
[SupportedOSPlatform("windows")]
[DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)]
private static extern void DnsFlushResolverCache();
/// <summary> /// <summary>
/// dns后台服务 /// dns后台服务
/// </summary> /// </summary>
/// <param name="requestResolver"></param> /// <param name="requestResolver"></param>
/// <param name="fastGithubConfig"></param> /// <param name="hostsValidator"></param>
/// <param name="options"></param> /// <param name="options"></param>
/// <param name="logger"></param> /// <param name="logger"></param>
public DnsServerHostedService( public DnsServerHostedService(
RequestResolver requestResolver, RequestResolver requestResolver,
FastGithubConfig fastGithubConfig,
HostsValidator hostsValidator, HostsValidator hostsValidator,
IOptionsMonitor<FastGithubOptions> options, IOptionsMonitor<FastGithubOptions> options,
ILogger<DnsServerHostedService> logger) ILogger<DnsServerHostedService> logger)
{ {
this.requestResolver = requestResolver; this.requestResolver = requestResolver;
this.fastGithubConfig = fastGithubConfig;
this.hostsValidator = hostsValidator; this.hostsValidator = hostsValidator;
this.logger = logger; this.logger = logger;
options.OnChange(opt => FlushResolverCache());
}
/// <summary>
/// 刷新dns缓存
/// </summary>
private static void FlushResolverCache()
{
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
DnsFlushResolverCache(); options.OnChange(opt => SystemDnsUtil.DnsFlushResolverCache());
} }
} }
/// <summary> /// <summary>
/// 启动dns /// 启动dns
/// </summary> /// </summary>
@ -70,57 +55,75 @@ namespace FastGithub.Dns
/// <returns></returns> /// <returns></returns>
public override async Task StartAsync(CancellationToken cancellationToken) public override async Task StartAsync(CancellationToken cancellationToken)
{ {
const int DNS_PORT = 53; await this.BindAsync(cancellationToken);
if (OperatingSystem.IsWindows() && UdpTable.TryGetOwnerProcessId(DNS_PORT, out var processId))
{
Process.GetProcessById(processId).Kill();
}
await BindAsync(this.socket, new IPEndPoint(IPAddress.Any, DNS_PORT), cancellationToken);
if (OperatingSystem.IsWindows())
{
const int SIO_UDP_CONNRESET = unchecked((int)0x9800000C);
this.socket.IOControl(SIO_UDP_CONNRESET, new byte[4], new byte[4]);
}
// 验证host文件
await this.hostsValidator.ValidateAsync(); await this.hostsValidator.ValidateAsync();
this.SetAsPrimitiveNameServer();
// 设置网关的dns
var secondary = this.fastGithubConfig.FastDns.Address;
this.dnsAddresses = this.SetNameServers(IPAddress.Loopback, secondary);
FlushResolverCache();
this.logger.LogInformation("dns服务启动成功"); this.logger.LogInformation("dns服务启动成功");
await base.StartAsync(cancellationToken); await base.StartAsync(cancellationToken);
} }
/// <summary> /// <summary>
/// 尝试多次绑定 /// 尝试多次绑定
/// </summary> /// </summary>
/// <param name="socket"></param>
/// <param name="localEndPoint"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private static async Task BindAsync(Socket socket, IPEndPoint localEndPoint, CancellationToken cancellationToken) private async Task BindAsync(CancellationToken cancellationToken)
{ {
const int DNS_PORT = 53;
if (OperatingSystem.IsWindows() && UdpTable.TryGetOwnerProcessId(DNS_PORT, out var processId))
{
Process.GetProcessById(processId).Kill();
}
var localEndPoint = new IPEndPoint(IPAddress.Any, DNS_PORT);
var delay = TimeSpan.FromMilliseconds(100d); var delay = TimeSpan.FromMilliseconds(100d);
for (var i = 10; i >= 0; i--) for (var i = 10; i >= 0; i--)
{ {
try try
{ {
socket.Bind(localEndPoint); this.socket.Bind(localEndPoint);
break; break;
} }
catch (Exception) catch (Exception)
{ {
if (i == 0) if (i == 0)
{ {
throw new FastGithubException($"无法监听{localEndPoint}{localEndPoint.Port}的udp端口已被其它程序占用"); throw new FastGithubException($"无法监听{localEndPoint}udp端口已被其它程序占用");
} }
await Task.Delay(delay, cancellationToken); await Task.Delay(delay, cancellationToken);
} }
} }
if (OperatingSystem.IsWindows())
{
const int SIO_UDP_CONNRESET = unchecked((int)0x9800000C);
this.socket.IOControl(SIO_UDP_CONNRESET, new byte[4], new byte[4]);
}
}
/// <summary>
/// 设置自身为主dns
/// </summary>
private void SetAsPrimitiveNameServer()
{
if (OperatingSystem.IsWindows())
{
try
{
SystemDnsUtil.DnsSetPrimitive(IPAddress.Loopback);
SystemDnsUtil.DnsFlushResolverCache();
this.logger.LogInformation($"设置为本机dns成功");
}
catch (Exception ex)
{
this.logger.LogWarning($"设置为本机dns失败{ex.Message}");
}
}
else
{
this.logger.LogWarning("平台不支持自动设置dns请手动设置网卡的主dns为127.0.0.1");
}
} }
/// <summary> /// <summary>
@ -178,40 +181,20 @@ namespace FastGithub.Dns
this.socket.Dispose(); this.socket.Dispose();
this.logger.LogInformation("dns服务已终止"); this.logger.LogInformation("dns服务已终止");
if (this.dnsAddresses != null)
{
this.SetNameServers(this.dnsAddresses);
}
FlushResolverCache();
return base.StopAsync(cancellationToken);
}
/// <summary>
/// 设置dns
/// </summary>
/// <param name="nameServers"></param>
/// <returns></returns>
private IPAddress[]? SetNameServers(params IPAddress[] nameServers)
{
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
try try
{ {
var results = SystemDnsUtil.SetNameServers(nameServers); SystemDnsUtil.DnsFlushResolverCache();
this.logger.LogInformation($"设置本机dns成功"); SystemDnsUtil.DnsRemovePrimitive(IPAddress.Loopback);
return results;
} }
catch (Exception ex) catch (Exception ex)
{ {
this.logger.LogWarning($"设置本机dns失败:{ex.Message}"); this.logger.LogWarning($"恢复DNS记录失败{ex.Message}");
} }
} }
else
{
this.logger.LogWarning("不支持自动设置dns请手动设置网卡的dns为127.0.0.1");
}
return default; return base.StopAsync(cancellationToken);
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -22,64 +23,96 @@ namespace FastGithub.Dns
[DllImport("iphlpapi")] [DllImport("iphlpapi")]
private static extern int GetBestInterface(uint dwDestAddr, ref uint pdwBestIfIndex); private static extern int GetBestInterface(uint dwDestAddr, ref uint pdwBestIfIndex);
/// <summary>
/// 刷新DNS缓存
/// </summary>
[DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)]
public static extern void DnsFlushResolverCache();
/// <summary> /// <summary>
/// 通过远程地址查找匹配的网络适接口 /// 通过远程地址查找匹配的网络适接口
/// </summary> /// </summary>
/// <param name="remoteAddress"></param> /// <param name="remoteAddress"></param>
/// <returns></returns> /// <returns></returns>
private static NetworkInterface? GetBestNetworkInterface(IPAddress remoteAddress) private static NetworkInterface GetBestNetworkInterface(IPAddress remoteAddress)
{ {
var dwBestIfIndex = 0u; var dwBestIfIndex = 0u;
var dwDestAddr = BitConverter.ToUInt32(remoteAddress.GetAddressBytes()); var dwDestAddr = BitConverter.ToUInt32(remoteAddress.GetAddressBytes());
var errorCode = GetBestInterface(dwDestAddr, ref dwBestIfIndex); var errorCode = GetBestInterface(dwDestAddr, ref dwBestIfIndex);
return errorCode != 0 if (errorCode != 0)
? throw new NetworkInformationException(errorCode) {
: NetworkInterface throw new NetworkInformationException(errorCode);
}
var @interface = NetworkInterface
.GetAllNetworkInterfaces() .GetAllNetworkInterfaces()
.Where(item => item.GetIPProperties().GetIPv4Properties().Index == dwBestIfIndex) .Where(item => item.GetIPProperties().GetIPv4Properties().Index == dwBestIfIndex)
.FirstOrDefault(); .FirstOrDefault();
return @interface ?? throw new NotSupportedException("找不到网络适配器用来设置dns");
}
/// <summary>
/// 设置主dns
/// </summary>
/// <param name="primitive"></param>
/// <exception cref="NetworkInformationException"></exception>
/// <exception cref="NotSupportedException"></exception>
public static void DnsSetPrimitive(IPAddress primitive)
{
var @interface = GetBestNetworkInterface(www_baidu_com);
var dnsAddresses = @interface.GetIPProperties().DnsAddresses;
if (primitive.Equals(dnsAddresses.FirstOrDefault()) == false)
{
var nameServers = dnsAddresses.Prepend(primitive);
SetNameServers(@interface, nameServers);
}
} }
/// <summary> /// <summary>
/// 设置域名服务 /// 移除主dns
/// </summary> /// </summary>
/// <param name="nameServers"></param> /// <param name="primitive"></param>
/// <exception cref="NetworkInformationException"></exception> /// <exception cref="NetworkInformationException"></exception>
/// <exception cref="NotSupportedException"></exception> /// <exception cref="NotSupportedException"></exception>
/// <returns>未设置之前的记录</returns> public static void DnsRemovePrimitive(IPAddress primitive)
public static IPAddress[] SetNameServers(params IPAddress[] nameServers)
{ {
var networkInterface = GetBestNetworkInterface(www_baidu_com); var @interface = GetBestNetworkInterface(www_baidu_com);
if (networkInterface == null) var dnsAddresses = @interface.GetIPProperties().DnsAddresses;
if (primitive.Equals(dnsAddresses.FirstOrDefault()))
{ {
throw new NotSupportedException("找不到网络适配器用来设置dns"); var nameServers = dnsAddresses.Skip(1);
SetNameServers(@interface, nameServers);
} }
var dnsAddresses = networkInterface.GetIPProperties().DnsAddresses.ToArray(); }
Netsh($@"interface ipv4 delete dns ""{networkInterface.Name}"" all"); /// <summary>
/// 设置网口的dns
/// </summary>
/// <param name="interface"></param>
/// <param name="nameServers"></param>
private static void SetNameServers(NetworkInterface @interface, IEnumerable<IPAddress> nameServers)
{
Netsh($@"interface ipv4 delete dns ""{@interface.Name}"" all");
foreach (var address in nameServers) foreach (var address in nameServers)
{ {
Netsh($@"interface ipv4 add dns ""{networkInterface.Name}"" {address} validate=no"); Netsh($@"interface ipv4 add dns ""{@interface.Name}"" {address} validate=no");
} }
return dnsAddresses; static void Netsh(string arguments)
}
/// <summary>
/// 执行Netsh
/// </summary>
/// <param name="arguments"></param>
private static void Netsh(string arguments)
{
var netsh = new ProcessStartInfo
{ {
FileName = "netsh.exe", var netsh = new ProcessStartInfo
Arguments = arguments, {
CreateNoWindow = true, FileName = "netsh.exe",
UseShellExecute = false, Arguments = arguments,
WindowStyle = ProcessWindowStyle.Hidden CreateNoWindow = true,
}; UseShellExecute = false,
Process.Start(netsh)?.WaitForExit(); WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(netsh)?.WaitForExit();
}
} }
} }
} }