Browse Source

强化dns客户端

陈国伟 3 năm trước cách đây
mục cha
commit
ad51a0a572

+ 56 - 3
FastGithub.DomainResolve/DnsClient.cs

@@ -2,13 +2,16 @@
 using DNS.Client.RequestResolver;
 using DNS.Protocol;
 using DNS.Protocol.ResourceRecords;
+using FastGithub.Configuration;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
 using System;
 using System.Collections.Concurrent;
+using System.Collections.Generic;
 using System.Linq;
 using System.Net;
+using System.Runtime.CompilerServices;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -21,6 +24,9 @@ namespace FastGithub.DomainResolve
     {
         private const int DNS_PORT = 53;
         private const string LOCALHOST = "localhost";
+
+        private readonly DnscryptProxy dnscryptProxy;
+        private readonly FastGithubConfig fastGithubConfig;
         private readonly ILogger<DnsClient> logger;
 
         private readonly ConcurrentDictionary<string, SemaphoreSlim> semaphoreSlims = new();
@@ -30,13 +36,60 @@ namespace FastGithub.DomainResolve
 
         /// <summary>
         /// DNS客户端
-        /// </summary> 
+        /// </summary>
+        /// <param name="dnscryptProxy"></param>
+        /// <param name="fastGithubConfig"></param>
         /// <param name="logger"></param>
-        public DnsClient(ILogger<DnsClient> logger)
+        public DnsClient(
+            DnscryptProxy dnscryptProxy,
+            FastGithubConfig fastGithubConfig,
+            ILogger<DnsClient> logger)
         {
+            this.dnscryptProxy = dnscryptProxy;
+            this.fastGithubConfig = fastGithubConfig;
             this.logger = logger;
         }
 
+        /// <summary>
+        /// 解析域名
+        /// </summary>
+        /// <param name="domain">域名</param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public async IAsyncEnumerable<IPAddress> ResolveAsync(string domain, [EnumeratorCancellation] CancellationToken cancellationToken)
+        {
+            var hashSet = new HashSet<IPAddress>();
+            foreach (var dns in this.GetDnsServers())
+            {
+                foreach (var address in await this.LookupAsync(dns, domain, cancellationToken))
+                {
+                    if (hashSet.Add(address) == true)
+                    {
+                        yield return address;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 获取dns服务
+        /// </summary>
+        /// <returns></returns>
+        private IEnumerable<IPEndPoint> GetDnsServers()
+        {
+            var cryptDns = this.dnscryptProxy.LocalEndPoint;
+            if (cryptDns != null)
+            {
+                yield return cryptDns;
+                yield return cryptDns;
+            }
+
+            foreach (var fallbackDns in this.fastGithubConfig.FallbackDns)
+            {
+                yield return fallbackDns;
+            }
+        }
+
         /// <summary>
         /// 解析域名
         /// </summary>
@@ -44,7 +97,7 @@ namespace FastGithub.DomainResolve
         /// <param name="domain"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        public async Task<IPAddress[]> LookupAsync(IPEndPoint dns, string domain, CancellationToken cancellationToken = default)
+        private async Task<IPAddress[]> LookupAsync(IPEndPoint dns, string domain, CancellationToken cancellationToken = default)
         {
             var key = $"{dns}:{domain}";
             var semaphore = this.semaphoreSlims.GetOrAdd(key, _ => new SemaphoreSlim(1, 1));

+ 4 - 43
FastGithub.DomainResolve/DomainResolver.cs

@@ -1,7 +1,6 @@
 using FastGithub.Configuration;
 using System.Collections.Generic;
 using System.Net;
-using System.Runtime.CompilerServices;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -12,23 +11,14 @@ namespace FastGithub.DomainResolve
     /// </summary> 
     sealed class DomainResolver : IDomainResolver
     {
-        private readonly DnscryptProxy dnscryptProxy;
-        private readonly FastGithubConfig fastGithubConfig;
         private readonly DnsClient dnsClient;
 
         /// <summary>
         /// 域名解析器
-        /// </summary>
-        /// <param name="dnscryptProxy"></param>
-        /// <param name="fastGithubConfig"></param>
+        /// </summary> 
         /// <param name="dnsClient"></param>
-        public DomainResolver(
-            DnscryptProxy dnscryptProxy,
-            FastGithubConfig fastGithubConfig,
-            DnsClient dnsClient)
+        public DomainResolver(DnsClient dnsClient)
         {
-            this.dnscryptProxy = dnscryptProxy;
-            this.fastGithubConfig = fastGithubConfig;
             this.dnsClient = dnsClient;
         }
 
@@ -53,38 +43,9 @@ namespace FastGithub.DomainResolve
         /// <param name="domain">域名</param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        public async IAsyncEnumerable<IPAddress> ResolveAllAsync(string domain, [EnumeratorCancellation] CancellationToken cancellationToken)
-        {
-            var hashSet = new HashSet<IPAddress>();
-            foreach (var dns in this.GetDnsServers())
-            {
-                foreach (var address in await this.dnsClient.LookupAsync(dns, domain, cancellationToken))
-                {
-                    if (hashSet.Add(address) == true)
-                    {
-                        yield return address;
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// 获取dns服务
-        /// </summary>
-        /// <returns></returns>
-        private IEnumerable<IPEndPoint> GetDnsServers()
+        public IAsyncEnumerable<IPAddress> ResolveAllAsync(string domain, CancellationToken cancellationToken)
         {
-            var cryptDns = this.dnscryptProxy.LocalEndPoint;
-            if (cryptDns != null)
-            {
-                yield return cryptDns;
-                yield return cryptDns;
-            }
-
-            foreach (var fallbackDns in this.fastGithubConfig.FallbackDns)
-            {
-                yield return fallbackDns;
-            }
+            return this.dnsClient.ResolveAsync(domain, cancellationToken);
         }
     }
 }

+ 50 - 0
FastGithub.DomainResolve/IPAddressItem.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Threading.Tasks;
+
+namespace FastGithub.DomainResolve
+{
+    sealed class IPAddressItem : IEquatable<IPAddressItem>
+    {
+        public IPAddress Address { get; }
+
+        public TimeSpan Elapsed { get; private set; } = TimeSpan.MaxValue;
+
+        public IPAddressItem(IPAddress address)
+        {
+            this.Address = address;
+        }
+
+        public async Task TestSpeedAsync()
+        {
+            try
+            {
+                using var ping = new Ping();
+                var reply = await ping.SendPingAsync(this.Address);
+                this.Elapsed = reply.Status == IPStatus.Success
+                    ? TimeSpan.FromMilliseconds(reply.RoundtripTime)
+                    : TimeSpan.MaxValue;
+            }
+            catch (Exception)
+            {
+                this.Elapsed = TimeSpan.MaxValue;
+            }
+        }
+
+        public bool Equals(IPAddressItem? other)
+        {
+            return other != null && other.Address.Equals(this.Address);
+        }
+
+        public override bool Equals(object? obj)
+        {
+            return obj is IPAddressItem other && this.Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            return this.Address.GetHashCode();
+        }
+    }
+}

+ 48 - 0
FastGithub.DomainResolve/IPAddressItemHashSet.cs

@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace FastGithub.DomainResolve
+{
+    sealed class IPAddressItemHashSet
+    {
+        private readonly object syncRoot = new();
+
+        private readonly HashSet<IPAddressItem> hashSet = new();
+
+        public int Count => this.hashSet.Count;
+
+        public bool Add(IPAddressItem item)
+        {
+            lock (this.syncRoot)
+            {
+                return this.hashSet.Add(item);
+            }
+        }
+
+        public void AddRange(IEnumerable<IPAddressItem> items)
+        {
+            lock (this.syncRoot)
+            {
+                foreach (var item in items)
+                {
+                    this.hashSet.Add(item);
+                }
+            }
+        }
+
+        public IPAddressItem[] ToArray()
+        {
+            lock (this.syncRoot)
+            {
+                return this.hashSet.ToArray();
+            }
+        }
+
+        public Task TestSpeedAsync()
+        {
+            var tasks = this.ToArray().Select(item => item.TestSpeedAsync());
+            return Task.WhenAll(tasks);
+        }
+    }
+}