diff --git a/FastGithub.Dns/DnsHostedService.cs b/FastGithub.Dns/DnsHostedService.cs new file mode 100644 index 0000000..37083c7 --- /dev/null +++ b/FastGithub.Dns/DnsHostedService.cs @@ -0,0 +1,114 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.Dns +{ + /// + /// dns后台服务 + /// + sealed class DnsHostedService : BackgroundService + { + private readonly DnsServer dnsServer; + private readonly HostsFileValidator hostsValidator; + private readonly ILogger logger; + + /// + /// dns后台服务 + /// + /// + /// + /// + /// + public DnsHostedService( + DnsServer dnsServer, + HostsFileValidator hostsValidator, + IOptionsMonitor options, + ILogger logger) + { + this.dnsServer = dnsServer; + this.hostsValidator = hostsValidator; + this.logger = logger; + + options.OnChange(opt => + { + if (OperatingSystem.IsWindows()) + { + SystemDnsUtil.DnsFlushResolverCache(); + } + }); + } + + /// + /// 启动dns + /// + /// + /// + public override async Task StartAsync(CancellationToken cancellationToken) + { + this.dnsServer.Bind(IPAddress.Any, 53); + this.logger.LogInformation("DNS服务启动成功"); + + 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"); + } + + await this.hostsValidator.ValidateAsync(); + await base.StartAsync(cancellationToken); + } + + /// + /// dns后台 + /// + /// + /// + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + return this.dnsServer.ListenAsync(stoppingToken); + } + + /// + /// 停止dns服务 + /// + /// + /// + public override Task StopAsync(CancellationToken cancellationToken) + { + this.dnsServer.Dispose(); + this.logger.LogInformation("DNS服务已停止"); + + if (OperatingSystem.IsWindows()) + { + try + { + SystemDnsUtil.DnsFlushResolverCache(); + SystemDnsUtil.DnsRemovePrimitive(IPAddress.Loopback); + } + catch (Exception ex) + { + this.logger.LogWarning($"恢复DNS记录失败:{ex.Message}"); + } + } + + return base.StopAsync(cancellationToken); + } + } +} diff --git a/FastGithub.Dns/DnsServer.cs b/FastGithub.Dns/DnsServer.cs new file mode 100644 index 0000000..a7c5650 --- /dev/null +++ b/FastGithub.Dns/DnsServer.cs @@ -0,0 +1,111 @@ +using DNS.Protocol; +using Microsoft.Extensions.Logging; +using System; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.Dns +{ + /// + /// dns服务器 + /// + sealed class DnsServer : IDisposable + { + private readonly RequestResolver requestResolver; + private readonly ILogger logger; + private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + private readonly byte[] buffer = new byte[ushort.MaxValue]; + + /// + /// dns服务器 + /// + /// + /// + public DnsServer( + RequestResolver requestResolver, + ILogger logger) + { + this.requestResolver = requestResolver; + this.logger = logger; + } + + /// + /// 绑定地址和端口 + /// + /// + /// + /// + public void Bind(IPAddress address, int port) + { + if (OperatingSystem.IsWindows() && UdpTable.TryGetOwnerProcessId(port, out var processId)) + { + Process.GetProcessById(processId).Kill(); + } + + if (OperatingSystem.IsWindows()) + { + const int SIO_UDP_CONNRESET = unchecked((int)0x9800000C); + this.socket.IOControl(SIO_UDP_CONNRESET, new byte[4], new byte[4]); + } + + this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + this.socket.Bind(new IPEndPoint(address, port)); + } + + /// + /// 监听dns请求 + /// + /// + /// + public async Task ListenAsync(CancellationToken cancellationToken) + { + var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + while (cancellationToken.IsCancellationRequested == false) + { + try + { + var result = await this.socket.ReceiveFromAsync(this.buffer, SocketFlags.None, remoteEndPoint); + var datas = new byte[result.ReceivedBytes]; + this.buffer.AsSpan(0, datas.Length).CopyTo(datas); + this.HandleRequestAsync(datas, result.RemoteEndPoint, cancellationToken); + } + catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) + { + break; + } + } + } + + /// + /// 处理dns请求 + /// + /// + /// + /// + private async void HandleRequestAsync(byte[] datas, EndPoint remoteEndPoint, CancellationToken cancellationToken) + { + try + { + var request = Request.FromArray(datas); + var remoteEndPointRequest = new RemoteEndPointRequest(request, remoteEndPoint); + var response = await this.requestResolver.Resolve(remoteEndPointRequest, cancellationToken); + await this.socket.SendToAsync(response.ToArray(), SocketFlags.None, remoteEndPoint); + } + catch (Exception ex) + { + this.logger.LogTrace($"处理DNS异常:{ex.Message}"); + } + } + + /// + /// 释放资源 + /// + public void Dispose() + { + this.socket.Dispose(); + } + } +} diff --git a/FastGithub.Dns/DnsServerHostedService.cs b/FastGithub.Dns/DnsServerHostedService.cs deleted file mode 100644 index ede083a..0000000 --- a/FastGithub.Dns/DnsServerHostedService.cs +++ /dev/null @@ -1,203 +0,0 @@ -using DNS.Protocol; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Diagnostics; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace FastGithub.Dns -{ - /// - /// dns后台服务 - /// - sealed class DnsServerHostedService : BackgroundService - { - private readonly RequestResolver requestResolver; - private readonly HostsFileValidator hostsValidator; - private readonly ILogger logger; - - private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - private readonly byte[] buffer = new byte[ushort.MaxValue]; - - - /// - /// dns后台服务 - /// - /// - /// - /// - /// - public DnsServerHostedService( - RequestResolver requestResolver, - HostsFileValidator hostsValidator, - IOptionsMonitor options, - ILogger logger) - { - this.requestResolver = requestResolver; - this.hostsValidator = hostsValidator; - this.logger = logger; - - options.OnChange(opt => - { - if (OperatingSystem.IsWindows()) - { - SystemDnsUtil.DnsFlushResolverCache(); - } - }); - } - - /// - /// 启动dns - /// - /// - /// - public override async Task StartAsync(CancellationToken cancellationToken) - { - await this.BindAsync(cancellationToken); - this.logger.LogInformation("DNS服务启动成功"); - - this.SetAsPrimitiveNameServer(); - await this.hostsValidator.ValidateAsync(); - - await base.StartAsync(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); - for (var i = 10; i >= 0; i--) - { - try - { - this.socket.Bind(localEndPoint); - break; - } - catch (Exception) - { - if (i == 0) - { - throw new FastGithubException($"无法监听{localEndPoint},udp端口已被其它程序占用"); - } - 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]); - } - } - - /// - /// 设置自身为主dns - /// - 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"); - } - } - - /// - /// dns后台 - /// - /// - /// - protected async override Task ExecuteAsync(CancellationToken stoppingToken) - { - var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - while (stoppingToken.IsCancellationRequested == false) - { - try - { - var result = await this.socket.ReceiveFromAsync(this.buffer, SocketFlags.None, remoteEndPoint); - var datas = new byte[result.ReceivedBytes]; - this.buffer.AsSpan(0, datas.Length).CopyTo(datas); - this.HandleRequestAsync(datas, result.RemoteEndPoint, stoppingToken); - } - catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) - { - break; - } - } - } - - /// - /// 处理dns请求 - /// - /// - /// - /// - private async void HandleRequestAsync(byte[] datas, EndPoint remoteEndPoint, CancellationToken cancellationToken) - { - try - { - var request = Request.FromArray(datas); - var remoteEndPointRequest = new RemoteEndPointRequest(request, remoteEndPoint); - var response = await this.requestResolver.Resolve(remoteEndPointRequest, cancellationToken); - await this.socket.SendToAsync(response.ToArray(), SocketFlags.None, remoteEndPoint); - } - catch (Exception ex) - { - this.logger.LogTrace($"处理DNS异常:{ex.Message}"); - } - } - - /// - /// 停止dns服务 - /// - /// - /// - public override Task StopAsync(CancellationToken cancellationToken) - { - this.socket.Dispose(); - this.logger.LogInformation("DNS服务已停止"); - - if (OperatingSystem.IsWindows()) - { - try - { - SystemDnsUtil.DnsFlushResolverCache(); - SystemDnsUtil.DnsRemovePrimitive(IPAddress.Loopback); - } - catch (Exception ex) - { - this.logger.LogWarning($"恢复DNS记录失败:{ex.Message}"); - } - } - - return base.StopAsync(cancellationToken); - } - } -} diff --git a/FastGithub.Dns/DnsServerServiceCollectionExtensions.cs b/FastGithub.Dns/DnsServiceCollectionExtensions.cs similarity index 78% rename from FastGithub.Dns/DnsServerServiceCollectionExtensions.cs rename to FastGithub.Dns/DnsServiceCollectionExtensions.cs index 6dad648..b80527f 100644 --- a/FastGithub.Dns/DnsServerServiceCollectionExtensions.cs +++ b/FastGithub.Dns/DnsServiceCollectionExtensions.cs @@ -6,7 +6,7 @@ namespace FastGithub /// /// dns服务注册扩展 /// - public static class DnsServerServiceCollectionExtensions + public static class DnsServiceCollectionExtensions { /// /// 注册dns服务 @@ -17,8 +17,9 @@ namespace FastGithub { return services .AddSingleton() + .AddSingleton() .AddSingleton() - .AddHostedService(); + .AddHostedService(); } } }