From 8adbacc16e1c03aa14463899fedd859a2782c00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9B=BD=E4=BC=9F?= <366193849@qq.com> Date: Thu, 16 Sep 2021 10:11:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=9E=E9=80=80dns=E4=BD=BF=E7=94=A8tcp?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- @libs/dnscrypt-proxy.toml | 4 +- FastGithub.DomainResolve/DomainResolver.cs | 104 +++++++++++++-------- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/@libs/dnscrypt-proxy.toml b/@libs/dnscrypt-proxy.toml index c74f14e..84d0bb2 100644 --- a/@libs/dnscrypt-proxy.toml +++ b/@libs/dnscrypt-proxy.toml @@ -86,7 +86,7 @@ disabled_server_names = [] ## (dnscrypt-proxy will always encrypt everything even using UDP), and can ## only increase latency. -force_tcp = false +force_tcp = true ## SOCKS proxy @@ -223,7 +223,7 @@ cert_refresh_delay = 240 ## ## If more than one resolver is specified, they will be tried in sequence. -fallback_resolvers = [] +fallback_resolvers = ['9.9.9.9:53', '8.8.8.8:53'] ## Always use the fallback resolver before the system DNS settings. diff --git a/FastGithub.DomainResolve/DomainResolver.cs b/FastGithub.DomainResolve/DomainResolver.cs index 5c51de0..427fae8 100644 --- a/FastGithub.DomainResolve/DomainResolver.cs +++ b/FastGithub.DomainResolve/DomainResolver.cs @@ -1,5 +1,6 @@ -using DNS.Client; +using DNS.Client.RequestResolver; using DNS.Protocol; +using DNS.Protocol.ResourceRecords; using FastGithub.Configuration; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; @@ -23,30 +24,29 @@ namespace FastGithub.DomainResolve private readonly IMemoryCache domainResolveCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); private readonly IMemoryCache disableIPAddressCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); - private readonly FastGithubConfig fastGithubConfig; private readonly DnscryptProxy dnscryptProxy; private readonly ILogger logger; + private readonly TimeSpan lookupTimeout = TimeSpan.FromSeconds(5d); private readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(5d); private readonly TimeSpan disableIPExpiration = TimeSpan.FromMinutes(2d); private readonly TimeSpan dnscryptExpiration = TimeSpan.FromMinutes(10d); - private readonly TimeSpan systemExpiration = TimeSpan.FromMinutes(2d); + private readonly TimeSpan fallbackExpiration = TimeSpan.FromMinutes(2d); private readonly TimeSpan loopbackExpiration = TimeSpan.FromSeconds(5d); + + private readonly IPEndPoint fallbackDns = new(IPAddress.Parse("114.114.114.114"), 53); private readonly ConcurrentDictionary semaphoreSlims = new(); /// /// 域名解析器 - /// - /// + /// /// /// public DomainResolver( - FastGithubConfig fastGithubConfig, DnscryptProxy dnscryptProxy, ILogger logger) { - this.fastGithubConfig = fastGithubConfig; this.dnscryptProxy = dnscryptProxy; this.logger = logger; } @@ -114,10 +114,10 @@ namespace FastGithub.DomainResolve address = await this.LookupByDnscryptAsync(domain, cancellationToken); } - if (address == null && OperatingSystem.IsWindows() == false) + if (address == null) { - expiration = this.systemExpiration; - address = await this.LookupByDnsSystemAsync(domain, cancellationToken); + expiration = this.fallbackExpiration; + address = await this.LookupByFallbackAsync(domain, cancellationToken); } if (address == null) @@ -150,57 +150,58 @@ namespace FastGithub.DomainResolve return null; } - try - { - var dnsClient = new DnsClient(dns); - var addresses = await dnsClient.Lookup(domain.Host, RecordType.A, cancellationToken); - var address = await this.FindFastValueAsync(addresses, domain.Port, cancellationToken); - if (address == null) - { - this.logger.LogWarning($"dns({dns})解析不到{domain.Host}可用的ip解析"); - } - else - { - this.logger.LogInformation($"dns({dns}): {domain.Host}->{address}"); - } - return address; - } - catch (Exception ex) - { - cancellationToken.ThrowIfCancellationRequested(); - this.logger.LogWarning($"dns({dns})无法解析{domain.Host}:{ex.Message}"); - return null; - } + var resolver = new RequestResolver(dns, forceTcp: false); + return await this.LookupAsync(resolver, domain, cancellationToken); } /// - /// 系统DNS查找ip + /// 回退查找ip /// /// /// /// /// - private async Task LookupByDnsSystemAsync(DnsEndPoint domain, CancellationToken cancellationToken) + private async Task LookupByFallbackAsync(DnsEndPoint domain, CancellationToken cancellationToken) + { + var resolver = new RequestResolver(this.fallbackDns, forceTcp: true); + return await this.LookupAsync(resolver, domain, cancellationToken); + } + + /// + /// 查找ip + /// + /// + /// + /// + /// + private async Task LookupAsync(IRequestResolver resolver, DnsEndPoint domain, CancellationToken cancellationToken) { try { - var allAddresses = await Dns.GetHostAddressesAsync(domain.Host); - var addresses = allAddresses.Where(item => item.AddressFamily == AddressFamily.InterNetwork); + var request = new Request(); + request.Questions.Add(new Question(new Domain(domain.Host), RecordType.A)); + + using var timeoutTokenSource = new CancellationTokenSource(this.lookupTimeout); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); + var response = await resolver.Resolve(request, linkedTokenSource.Token); + + var addresses = response.AnswerRecords.OfType().Select(item => item.IPAddress).ToArray(); var address = await this.FindFastValueAsync(addresses, domain.Port, cancellationToken); + if (address == null) { - this.logger.LogWarning($"dns(系统)解析不到{domain.Host}可用的ip解析"); + this.logger.LogWarning($"dns({resolver})解析不到{domain.Host}可用的ip解析"); } else { - this.logger.LogInformation($"dns(系统): {domain.Host}->{address}"); + this.logger.LogInformation($"dns({resolver}): {domain.Host}->{address}"); } return address; } catch (Exception ex) { cancellationToken.ThrowIfCancellationRequested(); - this.logger.LogWarning($"dns(系统)无法解析{domain.Host}:{ex.Message}"); + this.logger.LogWarning($"dns({resolver})无法解析{domain.Host}:{ex.Message}"); return default; } } @@ -268,5 +269,32 @@ namespace FastGithub.DomainResolve return default; } } + + /// + /// 请求解析器 + /// + private class RequestResolver : IRequestResolver + { + private readonly IPEndPoint dns; + private readonly IRequestResolver resolver; + + public RequestResolver(IPEndPoint dns, bool forceTcp) + { + this.dns = dns; + this.resolver = forceTcp + ? new TcpRequestResolver(dns) + : new UdpRequestResolver(dns, new TcpRequestResolver(dns)); + } + + public Task Resolve(IRequest request, CancellationToken cancellationToken = default) + { + return this.resolver.Resolve(request, cancellationToken); + } + + public override string ToString() + { + return this.dns.ToString(); + } + } } }