diff --git a/FastGithub.Core/DomainMatch.cs b/FastGithub.Core/DomainMatch.cs index 3e93c08..8c2d438 100644 --- a/FastGithub.Core/DomainMatch.cs +++ b/FastGithub.Core/DomainMatch.cs @@ -5,7 +5,7 @@ namespace FastGithub /// /// 域名匹配 /// - sealed class DomainMatch + public class DomainMatch { private readonly Regex regex; private readonly string domainPattern; diff --git a/FastGithub.Core/FastGithub.Core.csproj b/FastGithub.Core/FastGithub.Core.csproj index 8c0b46e..7075b28 100644 --- a/FastGithub.Core/FastGithub.Core.csproj +++ b/FastGithub.Core/FastGithub.Core.csproj @@ -3,6 +3,10 @@ net5.0 FastGithub - + + + + + diff --git a/FastGithub.Core/FastGithubConfig.cs b/FastGithub.Core/FastGithubConfig.cs index 7f9e748..eb04efb 100644 --- a/FastGithub.Core/FastGithubConfig.cs +++ b/FastGithub.Core/FastGithubConfig.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using Microsoft.Extensions.Options; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; @@ -10,27 +12,51 @@ namespace FastGithub /// public class FastGithubConfig { - private readonly Dictionary domainConfigs; + /// + /// 域名与配置缓存 + /// + [AllowNull] + private ConcurrentDictionary domainConfigCache; + /// /// 获取信任dns /// - public IPEndPoint TrustedDns { get; } + [AllowNull] + public IPEndPoint TrustedDns { get; private set; } /// /// 获取非信任dns /// - public IPEndPoint UnTrustedDns { get; } + [AllowNull] + public IPEndPoint UnTrustedDns { get; private set; } + + /// + /// 获取域名配置 + /// + [AllowNull] + public Dictionary DomainConfigs { get; private set; } /// /// FastGithub配置 + /// + /// + public FastGithubConfig(IOptionsMonitor options) + { + this.Init(options.CurrentValue); + options.OnChange(opt => this.Init(opt)); + } + + /// + /// 初始化 /// /// - public FastGithubConfig(FastGithubOptions options) + private void Init(FastGithubOptions options) { + this.domainConfigCache = new ConcurrentDictionary(); this.TrustedDns = options.TrustedDns.ToIPEndPoint(); this.UnTrustedDns = options.UntrustedDns.ToIPEndPoint(); - this.domainConfigs = options.DomainConfigs.ToDictionary(kv => new DomainMatch(kv.Key), kv => kv.Value); + this.DomainConfigs = options.DomainConfigs.ToDictionary(kv => new DomainMatch(kv.Key), kv => kv.Value); } /// @@ -40,24 +66,25 @@ namespace FastGithub /// public bool IsMatch(string domain) { - return this.domainConfigs.Keys.Any(item => item.IsMatch(domain)); + return this.TryGetDomainConfig(domain, out _); } /// /// 尝试获取域名配置 /// /// - /// + /// /// - public bool TryGetDomainConfig(string domain, [MaybeNullWhen(false)] out DomainConfig domainConfig) + public bool TryGetDomainConfig(string domain, [MaybeNullWhen(false)] out DomainConfig value) { - var key = this.domainConfigs.Keys.FirstOrDefault(item => item.IsMatch(domain)); - if (key == null) + value = this.domainConfigCache.GetOrAdd(domain, GetDomainConfig); + return value != null; + + DomainConfig? GetDomainConfig(string domain) { - domainConfig = default; - return false; + var key = this.DomainConfigs.Keys.FirstOrDefault(item => item.IsMatch(domain)); + return key == null ? null : this.DomainConfigs[key]; } - return this.domainConfigs.TryGetValue(key, out domainConfig); } } } diff --git a/FastGithub.Core/FastGithubOptions.cs b/FastGithub.Core/FastGithubOptions.cs index 3ab9e7c..ef78563 100644 --- a/FastGithub.Core/FastGithubOptions.cs +++ b/FastGithub.Core/FastGithubOptions.cs @@ -21,26 +21,5 @@ namespace FastGithub /// 代理的域名配置 /// public Dictionary DomainConfigs { get; set; } = new(); - - - - /// - /// 初始化选项为配置 - /// - /// - public void InitConfig() - { - this.fastGithubConfig = new FastGithubConfig(this); - } - - /// - /// 配置 - /// - private FastGithubConfig? fastGithubConfig; - - /// - /// 获取配置 - /// - public FastGithubConfig Config => this.fastGithubConfig!; } } diff --git a/FastGithub.Dns/DnsServerHostedService.cs b/FastGithub.Dns/DnsServerHostedService.cs index 7d1b7c0..10b10f4 100644 --- a/FastGithub.Dns/DnsServerHostedService.cs +++ b/FastGithub.Dns/DnsServerHostedService.cs @@ -19,7 +19,7 @@ namespace FastGithub.Dns sealed class DnsServerHostedService : BackgroundService { private readonly RequestResolver requestResolver; - private readonly IOptionsMonitor options; + private readonly FastGithubConfig fastGithubConfig; private readonly ILogger logger; private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); @@ -28,21 +28,23 @@ namespace FastGithub.Dns [SupportedOSPlatform("windows")] [DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)] - private static extern uint DnsFlushResolverCache(); + private static extern void DnsFlushResolverCache(); /// /// dns后台服务 /// /// + /// /// /// public DnsServerHostedService( RequestResolver requestResolver, + FastGithubConfig fastGithubConfig, IOptionsMonitor options, ILogger logger) { this.requestResolver = requestResolver; - this.options = options; + this.fastGithubConfig = fastGithubConfig; this.logger = logger; options.OnChange(opt => FlushResolverCache()); } @@ -80,7 +82,7 @@ namespace FastGithub.Dns } this.logger.LogInformation("dns服务启动成功"); - var secondary = options.CurrentValue.Config.UnTrustedDns.Address; + var secondary = this.fastGithubConfig.UnTrustedDns.Address; this.dnsAddresses = this.SetNameServers(IPAddress.Loopback, secondary); FlushResolverCache(); @@ -108,7 +110,7 @@ namespace FastGithub.Dns { if (i == 0) { - throw; + throw new FastGithubException($"无法监听{localEndPoint},{localEndPoint.Port}的udp端口已被其它程序占用"); } await Task.Delay(delay, cancellationToken); } diff --git a/FastGithub.Dns/RequestResolver.cs b/FastGithub.Dns/RequestResolver.cs index 4de57ea..e2ea448 100644 --- a/FastGithub.Dns/RequestResolver.cs +++ b/FastGithub.Dns/RequestResolver.cs @@ -16,23 +16,34 @@ namespace FastGithub.Dns /// sealed class RequestResolver : IRequestResolver { + private IRequestResolver requestResolver; + private readonly TimeSpan ttl = TimeSpan.FromMinutes(1d); - private readonly IRequestResolver untrustedResolver; - private readonly IOptionsMonitor options; + private readonly FastGithubConfig fastGithubConfig; private readonly ILogger logger; /// /// dns解析者 - /// + /// + /// /// /// public RequestResolver( + FastGithubConfig fastGithubConfig, IOptionsMonitor options, ILogger logger) { - this.options = options; + this.fastGithubConfig = fastGithubConfig; this.logger = logger; - this.untrustedResolver = new UdpRequestResolver(options.CurrentValue.Config.TrustedDns); + + this.requestResolver = new UdpRequestResolver(fastGithubConfig.UnTrustedDns); + options.OnChange(opt => DnsConfigChanged(opt.UntrustedDns)); + + void DnsConfigChanged(DnsConfig config) + { + var dns = config.ToIPEndPoint(); + this.requestResolver = new UdpRequestResolver(dns); + } } /// @@ -55,8 +66,9 @@ namespace FastGithub.Dns return response; } + // 解析匹配的域名指向本机ip var domain = question.Name; - if (this.options.CurrentValue.Config.IsMatch(domain.ToString()) == true) + if (this.fastGithubConfig.IsMatch(domain.ToString()) == true) { var localAddress = remoteEndPointRequest.GetLocalAddress() ?? IPAddress.Loopback; var record = new IPAddressResourceRecord(domain, localAddress, this.ttl); @@ -66,7 +78,7 @@ namespace FastGithub.Dns return response; } - return await this.untrustedResolver.Resolve(request, cancellationToken); + return await this.requestResolver.Resolve(request, cancellationToken); } } } diff --git a/FastGithub.ReverseProxy/DomainResolver.cs b/FastGithub.ReverseProxy/DomainResolver.cs index e8908e9..7b442ee 100644 --- a/FastGithub.ReverseProxy/DomainResolver.cs +++ b/FastGithub.ReverseProxy/DomainResolver.cs @@ -1,6 +1,5 @@ using DNS.Client; using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Options; using System; using System.Linq; using System.Net; @@ -10,24 +9,25 @@ using System.Threading.Tasks; namespace FastGithub.ReverseProxy { /// - /// 受信任的域名解析器 + /// 域名解析器 /// sealed class DomainResolver { private readonly IMemoryCache memoryCache; + private readonly FastGithubConfig fastGithubConfig; private readonly TimeSpan cacheTimeSpan = TimeSpan.FromSeconds(10d); - private readonly IOptionsMonitor options; /// - /// 受信任的域名解析器 - /// - /// + /// 域名解析器 + /// + /// + /// public DomainResolver( IMemoryCache memoryCache, - IOptionsMonitor options) + FastGithubConfig fastGithubConfig) { this.memoryCache = memoryCache; - this.options = options; + this.fastGithubConfig = fastGithubConfig; } /// @@ -55,22 +55,22 @@ namespace FastGithub.ReverseProxy /// private async Task LookupAsync(string domain, CancellationToken cancellationToken) { - var endpoint = this.options.CurrentValue.TrustedDns.ToIPEndPoint(); try { - var dnsClient = new DnsClient(endpoint); + var dns = this.fastGithubConfig.TrustedDns; + var dnsClient = new DnsClient(dns); var addresses = await dnsClient.Lookup(domain, DNS.Protocol.RecordType.A, cancellationToken); var address = addresses?.FirstOrDefault(); if (address == null) { - throw new FastGithubException($"dns({endpoint}):解析不到{domain}的ip"); + throw new FastGithubException($"dns({dns}):解析不到{domain}的ip"); } // 受干扰的dns,常常返回127.0.0.1来阻断请求 // 如果解析到的ip为本机ip,会产生反向代理请求死循环 if (address.Equals(IPAddress.Loopback)) { - throw new FastGithubException($"dns({endpoint}):解析{domain}被干扰为{address}"); + throw new FastGithubException($"dns({dns}):解析{domain}被干扰为{address}"); } return address; } @@ -80,7 +80,8 @@ namespace FastGithub.ReverseProxy } catch (Exception ex) { - throw new FastGithubException($"dns({endpoint}):{ex.Message}", ex); + var dns = this.fastGithubConfig.TrustedDns; + throw new FastGithubException($"dns({dns}):{ex.Message}", ex); } } } diff --git a/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs b/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs index f4015c0..851bcc6 100644 --- a/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs +++ b/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; using System; using System.Net.Http; using System.Threading.Tasks; @@ -15,18 +14,18 @@ namespace FastGithub.ReverseProxy private readonly IHttpForwarder httpForwarder; private readonly SniHttpClientHanlder sniHttpClientHanlder; private readonly NoSniHttpClientHanlder noSniHttpClientHanlder; - private readonly IOptionsMonitor options; + private readonly FastGithubConfig fastGithubConfig; public ReverseProxyMiddleware( IHttpForwarder httpForwarder, SniHttpClientHanlder sniHttpClientHanlder, NoSniHttpClientHanlder noSniHttpClientHanlder, - IOptionsMonitor options) + FastGithubConfig fastGithubConfig) { this.httpForwarder = httpForwarder; this.sniHttpClientHanlder = sniHttpClientHanlder; this.noSniHttpClientHanlder = noSniHttpClientHanlder; - this.options = options; + this.fastGithubConfig = fastGithubConfig; } /// @@ -37,7 +36,7 @@ namespace FastGithub.ReverseProxy public async Task InvokeAsync(HttpContext context) { var host = context.Request.Host.Host; - if (this.options.CurrentValue.Config.TryGetDomainConfig(host, out var domainConfig) == false) + if (this.fastGithubConfig.TryGetDomainConfig(host, out var domainConfig) == false) { await context.Response.WriteAsJsonAsync(new { diff --git a/FastGithub/Program.cs b/FastGithub/Program.cs index 0abbd6e..9e8fe5c 100644 --- a/FastGithub/Program.cs +++ b/FastGithub/Program.cs @@ -32,14 +32,12 @@ namespace FastGithub }) .ConfigureServices((ctx, services) => { - services - .AddAppUpgrade() - .AddDnsServer() - .AddReverseProxy() - .AddDnscryptProxy() - .AddOptions() - .Bind(ctx.Configuration.GetSection(nameof(FastGithub))) - .PostConfigure(opt => opt.InitConfig()); + services.AddAppUpgrade(); + services.AddDnsServer(); + services.AddReverseProxy(); + services.AddDnscryptProxy(); + services.AddSingleton(); + services.Configure(ctx.Configuration.GetSection(nameof(FastGithub))); }) .ConfigureWebHostDefaults(web => {