using DNS.Client; using DNS.Protocol; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using System; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; namespace FastGithub.DomainResolve { /// /// 域名解析器 /// sealed class DomainResolver : IDomainResolver { private readonly IMemoryCache memoryCache; private readonly FastGithubConfig fastGithubConfig; private readonly ILogger logger; private readonly TimeSpan cacheTimeSpan = TimeSpan.FromMinutes(1d); /// /// 域名解析器 /// /// /// /// public DomainResolver( IMemoryCache memoryCache, FastGithubConfig fastGithubConfig, ILogger logger) { this.memoryCache = memoryCache; this.fastGithubConfig = fastGithubConfig; this.logger = logger; } /// /// 解析指定的域名 /// /// /// /// public Task ResolveAsync(string domain, CancellationToken cancellationToken) { // 缓存以避免做不必要的并发查询 var key = $"{nameof(DomainResolver)}:{domain}"; return this.memoryCache.GetOrCreateAsync(key, e => { e.SetAbsoluteExpiration(this.cacheTimeSpan); return this.LookupAsync(domain, cancellationToken); }); } /// /// 查找ip /// /// /// /// /// private async Task LookupAsync(string domain, CancellationToken cancellationToken) { var pureDns = this.fastGithubConfig.PureDns; var fastDns = this.fastGithubConfig.FastDns; try { return await LookupCoreAsync(pureDns, domain, cancellationToken); } catch (Exception) { this.logger.LogTrace($"由于{pureDns}解析{domain}失败,本次使用{fastDns}"); return await LookupCoreAsync(fastDns, domain, cancellationToken); } } /// /// 查找ip /// /// /// /// /// private async Task LookupCoreAsync(IPEndPoint dns, string domain, CancellationToken cancellationToken) { var dnsClient = new DnsClient(dns); using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1d)); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); var addresses = await dnsClient.Lookup(domain, RecordType.A, linkedTokenSource.Token); var address = addresses?.FirstOrDefault(); if (address == null) { throw new FastGithubException($"dns{dns}解析不到{domain}的ip"); } // 受干扰的dns,常常返回127.0.0.1来阻断请求 if (address.Equals(IPAddress.Loopback)) { throw new FastGithubException($"dns{dns}被污染,解析{domain}为{address}"); } this.logger.LogInformation($"[{domain}->{address}]"); return address; } } }