소스 검색

增加SocketTimedOut的ip黑名单处理

老九 4 년 전
부모
커밋
d9034477da

+ 37 - 12
FastGithub.DomainResolve/DomainResolver.cs

@@ -3,6 +3,7 @@ using DNS.Protocol;
 using FastGithub.Configuration;
 using FastGithub.Configuration;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Generic;
@@ -19,7 +20,9 @@ namespace FastGithub.DomainResolve
     /// </summary> 
     /// </summary> 
     sealed class DomainResolver : IDomainResolver
     sealed class DomainResolver : IDomainResolver
     {
     {
-        private readonly IMemoryCache memoryCache;
+        private readonly IMemoryCache blackIPAddressCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
+        private readonly IMemoryCache domainResolveCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
+
         private readonly FastGithubConfig fastGithubConfig;
         private readonly FastGithubConfig fastGithubConfig;
         private readonly DnscryptProxy dnscryptProxy;
         private readonly DnscryptProxy dnscryptProxy;
         private readonly ILogger<DomainResolver> logger;
         private readonly ILogger<DomainResolver> logger;
@@ -33,23 +36,40 @@ namespace FastGithub.DomainResolve
 
 
         /// <summary>
         /// <summary>
         /// 域名解析器
         /// 域名解析器
-        /// </summary>
-        /// <param name="memoryCache"></param>
+        /// </summary> 
         /// <param name="fastGithubConfig"></param>
         /// <param name="fastGithubConfig"></param>
         /// <param name="dnscryptProxy"></param>
         /// <param name="dnscryptProxy"></param>
         /// <param name="logger"></param>
         /// <param name="logger"></param>
         public DomainResolver(
         public DomainResolver(
-            IMemoryCache memoryCache,
             FastGithubConfig fastGithubConfig,
             FastGithubConfig fastGithubConfig,
             DnscryptProxy dnscryptProxy,
             DnscryptProxy dnscryptProxy,
             ILogger<DomainResolver> logger)
             ILogger<DomainResolver> logger)
         {
         {
-            this.memoryCache = memoryCache;
             this.fastGithubConfig = fastGithubConfig;
             this.fastGithubConfig = fastGithubConfig;
             this.dnscryptProxy = dnscryptProxy;
             this.dnscryptProxy = dnscryptProxy;
             this.logger = logger;
             this.logger = logger;
         }
         }
 
 
+        /// <summary>
+        /// 设置ip黑名单
+        /// </summary>
+        /// <param name="address">ip</param>
+        /// <param name="expiration">过期时间</param>
+        public void SetBlack(IPAddress address, TimeSpan expiration)
+        {
+            this.blackIPAddressCache.Set(address, address, expiration);
+            this.logger.LogWarning($"已自动将{address}关到黑屋{expiration}");
+        }
+
+        /// <summary>
+        /// 刷新域名解析结果
+        /// </summary>
+        /// <param name="domain">域名</param>
+        public void FlushDomain(DnsEndPoint domain)
+        {
+            this.domainResolveCache.Remove(domain);
+        }
+
         /// <summary>
         /// <summary>
         /// 解析域名
         /// 解析域名
         /// </summary>
         /// </summary>
@@ -78,7 +98,7 @@ namespace FastGithub.DomainResolve
         /// <returns></returns>
         /// <returns></returns>
         private async Task<IPAddress> LookupAsync(DnsEndPoint domain, CancellationToken cancellationToken)
         private async Task<IPAddress> LookupAsync(DnsEndPoint domain, CancellationToken cancellationToken)
         {
         {
-            if (this.memoryCache.TryGetValue<IPAddress>(domain, out var address))
+            if (this.domainResolveCache.TryGetValue<IPAddress>(domain, out var address))
             {
             {
                 return address;
                 return address;
             }
             }
@@ -107,7 +127,7 @@ namespace FastGithub.DomainResolve
             }
             }
 
 
             this.logger.LogInformation($"[{domain.Host}->{address}]");
             this.logger.LogInformation($"[{domain.Host}->{address}]");
-            this.memoryCache.Set(domain, address, expiration);
+            this.domainResolveCache.Set(domain, address, expiration);
             return address;
             return address;
         }
         }
 
 
@@ -170,11 +190,6 @@ namespace FastGithub.DomainResolve
                 return default;
                 return default;
             }
             }
 
 
-            if (port <= 0)
-            {
-                return addresses.FirstOrDefault();
-            }
-
             var tasks = addresses.Select(address => this.IsAvailableAsync(address, port, cancellationToken));
             var tasks = addresses.Select(address => this.IsAvailableAsync(address, port, cancellationToken));
             var fastTask = await Task.WhenAny(tasks);
             var fastTask = await Task.WhenAny(tasks);
             return await fastTask;
             return await fastTask;
@@ -190,6 +205,16 @@ namespace FastGithub.DomainResolve
         /// <returns></returns>
         /// <returns></returns>
         private async Task<IPAddress?> IsAvailableAsync(IPAddress address, int port, CancellationToken cancellationToken)
         private async Task<IPAddress?> IsAvailableAsync(IPAddress address, int port, CancellationToken cancellationToken)
         {
         {
+            if (port <= 0)
+            {
+                return address;
+            }
+
+            if (this.blackIPAddressCache.TryGetValue(address, out _))
+            {
+                return default;
+            }
+
             try
             try
             {
             {
                 using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                 using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);

+ 16 - 2
FastGithub.DomainResolve/IDomainResolver.cs

@@ -1,4 +1,5 @@
-using System.Net;
+using System;
+using System.Net;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
@@ -9,10 +10,23 @@ namespace FastGithub.DomainResolve
     /// </summary>
     /// </summary>
     public interface IDomainResolver
     public interface IDomainResolver
     {
     {
+        /// <summary>
+        /// 设置ip黑名单
+        /// </summary>
+        /// <param name="address">ip</param>
+        /// <param name="expiration">过期时间</param>
+        void SetBlack(IPAddress address, TimeSpan expiration);
+
+        /// <summary>
+        /// 刷新域名解析结果
+        /// </summary>
+        /// <param name="domain">域名</param>
+        void FlushDomain(DnsEndPoint domain);
+
         /// <summary>
         /// <summary>
         /// 解析域名
         /// 解析域名
         /// </summary>
         /// </summary>
-        /// <param name="domain"></param>
+        /// <param name="domain">域名</param>
         /// <param name="cancellationToken"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         /// <returns></returns>
         Task<IPAddress> ResolveAsync(DnsEndPoint domain, CancellationToken cancellationToken = default);
         Task<IPAddress> ResolveAsync(DnsEndPoint domain, CancellationToken cancellationToken = default);

+ 1 - 2
FastGithub.DomainResolve/ServiceCollectionExtensions.cs

@@ -15,8 +15,7 @@ namespace FastGithub
         /// <param name="services"></param> 
         /// <param name="services"></param> 
         /// <returns></returns>
         /// <returns></returns>
         public static IServiceCollection AddDomainResolve(this IServiceCollection services)
         public static IServiceCollection AddDomainResolve(this IServiceCollection services)
-        {
-            services.AddMemoryCache();
+        { 
             services.TryAddSingleton<DnscryptProxy>();
             services.TryAddSingleton<DnscryptProxy>();
             services.TryAddSingleton<IDomainResolver, DomainResolver>();
             services.TryAddSingleton<IDomainResolver, DomainResolver>();
             return services.AddHostedService<DnscryptProxyHostedService>();
             return services.AddHostedService<DnscryptProxyHostedService>();

+ 37 - 2
FastGithub.Http/HttpClientHandler.cs

@@ -21,6 +21,7 @@ namespace FastGithub.Http
     {
     {
         private readonly DomainConfig domainConfig;
         private readonly DomainConfig domainConfig;
         private readonly IDomainResolver domainResolver;
         private readonly IDomainResolver domainResolver;
+        private readonly TimeSpan blackIPAddressExpiration = TimeSpan.FromMinutes(10d);
 
 
         /// <summary>
         /// <summary>
         /// HttpClientHandler
         /// HttpClientHandler
@@ -42,8 +43,16 @@ namespace FastGithub.Http
         /// <returns></returns>
         /// <returns></returns>
         protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
         protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
         {
         {
-            await this.ProcessRequestAsync(request, cancellationToken);
-            return await this.SendRequestAsync(request, cancellationToken);
+            try
+            {
+                await this.ProcessRequestAsync(request, cancellationToken);
+                return await this.SendRequestAsync(request, cancellationToken);
+            }
+            catch (HttpRequestException ex)
+            {
+                this.InterceptRequestException(request, ex);
+                throw;
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -109,6 +118,32 @@ namespace FastGithub.Http
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// 拦截请求异常
+        /// 查找TimedOut的ip地址添加到黑名单
+        /// </summary>
+        /// <param name="request"></param>
+        /// <param name="exception"></param>
+        private void InterceptRequestException(HttpRequestMessage request, HttpRequestException exception)
+        {
+            if (request.RequestUri == null ||
+                exception.InnerException is not SocketException socketException ||
+                socketException.SocketErrorCode != SocketError.TimedOut)
+            {
+                return;
+            }
+
+            if (IPAddress.TryParse(request.RequestUri.Host, out var address))
+            {
+                this.domainResolver.SetBlack(address, this.blackIPAddressExpiration);
+            }
+
+            if (request.Headers.Host != null)
+            {
+                this.domainResolver.FlushDomain(new DnsEndPoint(request.Headers.Host, request.RequestUri.Port));
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// 创建转发代理的httpHandler
         /// 创建转发代理的httpHandler
         /// </summary>
         /// </summary>