diff --git a/FastGithub.Configuration/Class1.cs b/FastGithub.Configuration/Class1.cs new file mode 100644 index 0000000..f26a0e2 --- /dev/null +++ b/FastGithub.Configuration/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace FastGithub.Configuration +{ + public class Class1 + { + } +} diff --git a/FastGithub.Configuration/FastGithub.Configuration.csproj b/FastGithub.Configuration/FastGithub.Configuration.csproj new file mode 100644 index 0000000..f208d30 --- /dev/null +++ b/FastGithub.Configuration/FastGithub.Configuration.csproj @@ -0,0 +1,7 @@ + + + + net5.0 + + + diff --git a/FastGithub.Core/DnsIPEndPoint.cs b/FastGithub.Core/DnsIPEndPoint.cs new file mode 100644 index 0000000..640d2e8 --- /dev/null +++ b/FastGithub.Core/DnsIPEndPoint.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using System.Net; + +namespace FastGithub +{ + public class DnsIPEndPoint + { + [AllowNull] + public string Address { get; set; } = IPAddress.Loopback.ToString(); + + public int Port { get; set; } = 53; + + public IPEndPoint ToIPEndPoint() + { + return new IPEndPoint(IPAddress.Parse(this.Address), this.Port); + } + + public bool Validate() + { + return IPAddress.TryParse(this.Address, out var address) && + !(address.Equals(IPAddress.Loopback) && this.Port == 53); + } + } +} diff --git a/FastGithub.Core/FastGithub.Core.csproj b/FastGithub.Core/FastGithub.Core.csproj index e89fd2b..8c0b46e 100644 --- a/FastGithub.Core/FastGithub.Core.csproj +++ b/FastGithub.Core/FastGithub.Core.csproj @@ -3,10 +3,6 @@ net5.0 FastGithub - - - - - - + + diff --git a/FastGithub.Core/FastGithubOptions.cs b/FastGithub.Core/FastGithubOptions.cs new file mode 100644 index 0000000..148f04d --- /dev/null +++ b/FastGithub.Core/FastGithubOptions.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace FastGithub +{ + public class FastGithubOptions + { + private DomainMatch[]? domainMatches; + + public DnsIPEndPoint TrustedDns { get; set; } = new DnsIPEndPoint { Address = "127.0.0.1", Port = 5533 }; + + public DnsIPEndPoint UntrustedDns { get; set; } = new DnsIPEndPoint { Address = "114.1114.114.114", Port = 53 }; + + public HashSet DomainMatches { get; set; } = new(); + + public bool IsMatch(string domain) + { + if (this.domainMatches == null) + { + this.domainMatches = this.DomainMatches.Select(item => new DomainMatch(item)).ToArray(); + } + return this.domainMatches.Any(item => item.IsMatch(domain)); + } + + private class DomainMatch + { + private readonly Regex regex; + private readonly string value; + + public DomainMatch(string value) + { + this.value = value; + var pattern = Regex.Escape(value).Replace(@"\*", ".*"); + this.regex = new Regex($"^{pattern}$"); + } + + public bool IsMatch(string domain) + { + return this.regex.IsMatch(domain); + } + + public override string ToString() + { + return this.value; + } + } + } +} diff --git a/FastGithub.Core/IMiddleware.cs b/FastGithub.Core/IMiddleware.cs deleted file mode 100644 index 5eeb445..0000000 --- a/FastGithub.Core/IMiddleware.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace FastGithub -{ - /// - /// 定义中间件的接口 - /// - /// - public interface IMiddleware - { - /// - /// 执行中间件 - /// - /// 上下文 - /// 下一个中间件 - /// - Task InvokeAsync(TContext context, Func next); - } -} diff --git a/FastGithub.Core/IPipelineBuilder.cs b/FastGithub.Core/IPipelineBuilder.cs deleted file mode 100644 index 2c68118..0000000 --- a/FastGithub.Core/IPipelineBuilder.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace FastGithub -{ - /// - /// 定义中间件管道创建者的接口 - /// - /// 中间件上下文 - public interface IPipelineBuilder - { - /// - /// 获取服务提供者 - /// - IServiceProvider AppServices { get; } - - /// - /// 使用中间件 - /// - /// - /// - /// - /// - IPipelineBuilder Use() where TMiddleware : class, IMiddleware; - - /// - /// 使用中间件 - /// - /// - /// - /// - /// - IPipelineBuilder Use(Func, Task> middleware); - - /// - /// 使用中间件 - /// - /// 中间件 - /// - IPipelineBuilder Use(Func, InvokeDelegate> middleware); - - /// - /// 创建所有中间件执行处理者 - /// - /// - InvokeDelegate Build(); - - /// - /// 使用默认配制创建新的PipelineBuilder - /// - /// - IPipelineBuilder New(); - } -} diff --git a/FastGithub.Core/InvokeDelegate.cs b/FastGithub.Core/InvokeDelegate.cs deleted file mode 100644 index 985d433..0000000 --- a/FastGithub.Core/InvokeDelegate.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading.Tasks; - -namespace FastGithub -{ - /// - /// 表示所有中间件执行委托 - /// - /// 中间件上下文类型 - /// 中间件上下文 - /// - public delegate Task InvokeDelegate(TContext context); -} diff --git a/FastGithub.Core/OptionsAttribute.cs b/FastGithub.Core/OptionsAttribute.cs deleted file mode 100644 index a123be5..0000000 --- a/FastGithub.Core/OptionsAttribute.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace FastGithub -{ - /// - /// 表示选项特性 - /// - [AttributeUsage(AttributeTargets.Class)] - public sealed class OptionsAttribute : Attribute - { - /// - /// 获取配置节点名称 - /// - public string? SessionKey { get; } - - /// - /// 选项特性 - /// - public OptionsAttribute() - { - } - - /// - /// 选项特性 - /// - /// 配置节点名称 - public OptionsAttribute(string sessionKey) - { - this.SessionKey = sessionKey; - } - } -} diff --git a/FastGithub.Core/PipelineBuilder.cs b/FastGithub.Core/PipelineBuilder.cs deleted file mode 100644 index 8e0f017..0000000 --- a/FastGithub.Core/PipelineBuilder.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace FastGithub -{ - /// - /// 表示中间件创建者 - /// - public class PipelineBuilder : IPipelineBuilder - { - private readonly InvokeDelegate completedHandler; - private readonly List, InvokeDelegate>> middlewares = new(); - - /// - /// 获取服务提供者 - /// - public IServiceProvider AppServices { get; } - - /// - /// 中间件创建者 - /// - /// - public PipelineBuilder(IServiceProvider appServices) - : this(appServices, context => Task.CompletedTask) - { - } - - /// - /// 中间件创建者 - /// - /// - /// 完成执行内容处理者 - public PipelineBuilder(IServiceProvider appServices, InvokeDelegate completedHandler) - { - this.AppServices = appServices; - this.completedHandler = completedHandler; - } - - - /// - /// 使用中间件 - /// - /// - /// - /// - /// - public IPipelineBuilder Use() where TMiddleware : class, IMiddleware - { - var middleware = this.AppServices.GetRequiredService(); - return this.Use(middleware.InvokeAsync); - } - - /// - /// 使用中间件 - /// - /// - /// - /// - /// - public IPipelineBuilder Use(Func, Task> middleware) - { - return this.Use(next => context => middleware(context, () => next(context))); - } - - /// - /// 使用中间件 - /// - /// - /// - public IPipelineBuilder Use(Func, InvokeDelegate> middleware) - { - this.middlewares.Add(middleware); - return this; - } - - - /// - /// 创建所有中间件执行处理者 - /// - /// - public InvokeDelegate Build() - { - var handler = this.completedHandler; - for (var i = this.middlewares.Count - 1; i >= 0; i--) - { - handler = this.middlewares[i](handler); - } - return handler; - } - - - /// - /// 使用默认配制创建新的PipelineBuilder - /// - /// - public IPipelineBuilder New() - { - return new PipelineBuilder(this.AppServices, this.completedHandler); - } - } -} \ No newline at end of file diff --git a/FastGithub.Core/PipelineBuilderExtensions.cs b/FastGithub.Core/PipelineBuilderExtensions.cs deleted file mode 100644 index 67a585b..0000000 --- a/FastGithub.Core/PipelineBuilderExtensions.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; - -namespace FastGithub -{ - /// - /// 中间件创建者扩展 - /// - public static class PipelineBuilderExtensions - { - /// - /// 中断执行中间件 - /// - /// - /// - /// 处理者 - /// - public static IPipelineBuilder Run(this IPipelineBuilder builder, InvokeDelegate handler) - { - return builder.Use(_ => handler); - } - - /// - /// 条件中间件 - /// - /// - /// - /// - /// - /// - public static IPipelineBuilder When(this IPipelineBuilder builder, Func predicate, InvokeDelegate handler) - { - return builder.Use(next => async context => - { - if (predicate.Invoke(context) == true) - { - await handler.Invoke(context); - } - else - { - await next(context); - } - }); - } - - - /// - /// 条件中间件 - /// - /// - /// - /// - /// - /// - public static IPipelineBuilder When(this IPipelineBuilder builder, Func predicate, Action> configureAction) - { - return builder.Use(next => async context => - { - if (predicate.Invoke(context) == true) - { - var branchBuilder = builder.New(); - configureAction(branchBuilder); - await branchBuilder.Build().Invoke(context); - } - else - { - await next(context); - } - }); - } - } -} diff --git a/FastGithub.Core/ServiceAttribute.cs b/FastGithub.Core/ServiceAttribute.cs deleted file mode 100644 index be8f59d..0000000 --- a/FastGithub.Core/ServiceAttribute.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace FastGithub -{ - /// - /// 表示服务特性 - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public sealed class ServiceAttribute : Attribute - { - /// - /// 获取服务的生命周期 - /// - public ServiceLifetime Lifetime { get; } - - /// - /// 获取或设置注册的服务类型 - /// 为null直接使得当前类型 - /// - public Type? ServiceType { get; set; } - - /// - /// 将当前实现类型注册为服务的特性 - /// - /// 生命周期 - public ServiceAttribute(ServiceLifetime lifetime) - { - Lifetime = lifetime; - } - } -} diff --git a/FastGithub.Core/ServiceCollectionExtensions.cs b/FastGithub.Core/ServiceCollectionExtensions.cs deleted file mode 100644 index 7b0e744..0000000 --- a/FastGithub.Core/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Linq; -using System.Reflection; - -namespace FastGithub -{ - /// - /// 服务注册扩展 - /// - public static class ServiceCollectionExtensions - { - /// - /// 注册程序集下所有服务下选项 - /// - /// - /// 配置 - /// - public static IServiceCollection AddServiceAndOptions(this IServiceCollection services, Assembly assembly, IConfiguration configuration) - { - services.AddAttributeServices(assembly); - services.AddAttributeOptions(assembly, configuration); - - return services; - } - - /// - /// 添加程序集下ServiceAttribute标记的服务 - /// - /// - /// - /// - private static IServiceCollection AddAttributeServices(this IServiceCollection services, Assembly assembly) - { - var implTypes = assembly - .GetTypes() - .Where(item => item.IsClass && item.IsAbstract == false) - .ToArray(); - - foreach (var implType in implTypes) - { - var attributes = implType.GetCustomAttributes(false); - foreach (var attr in attributes) - { - var serviceType = attr.ServiceType ?? implType; - if (services.Any(item => item.ServiceType == serviceType && item.ImplementationType == implType) == false) - { - var descriptor = ServiceDescriptor.Describe(serviceType, implType, attr.Lifetime); - services.Add(descriptor); - } - } - } - return services; - } - - - /// - /// 添加程序集下OptionsAttribute标记的服务 - /// - /// - /// - /// - private static IServiceCollection AddAttributeOptions(this IServiceCollection services, Assembly assembly, IConfiguration configuration) - { - foreach (var optionsType in assembly.GetTypes()) - { - var optionsAttribute = optionsType.GetCustomAttribute(); - if (optionsAttribute != null) - { - var key = optionsAttribute.SessionKey ?? optionsType.Name; - var section = configuration.GetSection(key); - OptionsBinder.Create(services, optionsType).Bind(section); - } - } - return services; - } - - /// - /// options绑定器 - /// - private abstract class OptionsBinder - { - public abstract void Bind(IConfiguration configuration); - - /// - /// 创建OptionsBinder实例 - /// - /// - /// - /// - public static OptionsBinder Create(IServiceCollection services, Type optionsType) - { - var binderType = typeof(OptionsBinderImpl<>).MakeGenericType(optionsType); - var binder = Activator.CreateInstance(binderType, new object[] { services }); - - return binder is OptionsBinder optionsBinder - ? optionsBinder - : throw new TypeInitializationException(binderType.FullName, null); - } - - private class OptionsBinderImpl : OptionsBinder where TOptions : class - { - private readonly IServiceCollection services; - - public OptionsBinderImpl(IServiceCollection services) - { - this.services = services; - } - - public override void Bind(IConfiguration configuration) - { - this.services.AddOptions().Bind(configuration); - } - } - } - } -} diff --git a/FastGithub.Dns.Configuration/Class1.cs b/FastGithub.Dns.Configuration/Class1.cs new file mode 100644 index 0000000..a32a9ef --- /dev/null +++ b/FastGithub.Dns.Configuration/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace FastGithub.Dns.Configuration +{ + public class Class1 + { + } +} diff --git a/FastGithub.Dns.Configuration/FastGithub.Dns.Configuration.csproj b/FastGithub.Dns.Configuration/FastGithub.Dns.Configuration.csproj new file mode 100644 index 0000000..f208d30 --- /dev/null +++ b/FastGithub.Dns.Configuration/FastGithub.Dns.Configuration.csproj @@ -0,0 +1,7 @@ + + + + net5.0 + + + diff --git a/FastGithub.Dns/DnsOptions.cs b/FastGithub.Dns/DnsOptions.cs deleted file mode 100644 index f34f524..0000000 --- a/FastGithub.Dns/DnsOptions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; - -namespace FastGithub.Dns -{ - /// - /// dns服务选项 - /// - [Options("Dns")] - sealed class DnsOptions - { - /// - /// 获取或设置上游ip地址 - /// - public string UpStream { get; set; } = "114.114.114.114"; - - /// - /// 获取或设置github相关域名的ip存活时长 - /// - public TimeSpan GithubTTL { get; set; } = TimeSpan.FromMinutes(10d); - - /// - /// 是否设置本机使用此dns - /// - public bool SetToLocalMachine { get; set; } = true; - - /// - /// 是否使用反向代理访问github - /// - public bool UseGithubReverseProxy { get; set; } = true; - } -} diff --git a/FastGithub.Dns/DnsServerHostedService.cs b/FastGithub.Dns/DnsServerHostedService.cs index 499eebc..aa2ce82 100644 --- a/FastGithub.Dns/DnsServerHostedService.cs +++ b/FastGithub.Dns/DnsServerHostedService.cs @@ -1,5 +1,4 @@ -using DNS.Client.RequestResolver; -using DNS.Protocol; +using DNS.Protocol; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -18,15 +17,14 @@ namespace FastGithub.Dns { private const int SIO_UDP_CONNRESET = unchecked((int)0x9800000C); - private readonly IRequestResolver requestResolver; - private readonly IOptions options; + private readonly FastGihubResolver fastGihubResolver; + private readonly IOptions options; private readonly ILogger logger; private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); private readonly byte[] buffer = new byte[ushort.MaxValue]; private IPAddress[]? dnsAddresses; - /// /// dns后台服务 /// @@ -34,15 +32,13 @@ namespace FastGithub.Dns /// /// public DnsServerHostedService( - GithubRequestResolver githubRequestResolver, - IOptions options, + FastGihubResolver fastGihubResolver, + IOptions options, ILogger logger) { + this.fastGihubResolver = fastGihubResolver; this.options = options; this.logger = logger; - - var upStream = IPAddress.Parse(options.Value.UpStream); - this.requestResolver = new CompositeRequestResolver(upStream, githubRequestResolver); } /// @@ -59,7 +55,7 @@ namespace FastGithub.Dns } this.logger.LogInformation("dns服务启动成功"); - var upStream = IPAddress.Parse(options.Value.UpStream); + var upStream = IPAddress.Parse(options.Value.UntrustedDns.Address); this.dnsAddresses = this.SetNameServers(IPAddress.Loopback, upStream); return base.StartAsync(cancellationToken); } @@ -93,7 +89,7 @@ namespace FastGithub.Dns { var request = Request.FromArray(datas); var remoteRequest = new RemoteRequest(request, remoteEndPoint); - var response = await this.requestResolver.Resolve(remoteRequest, cancellationToken); + var response = await this.fastGihubResolver.Resolve(remoteRequest, cancellationToken); await this.socket.SendToAsync(response.ToArray(), SocketFlags.None, remoteEndPoint); } catch (Exception ex) @@ -126,7 +122,7 @@ namespace FastGithub.Dns /// private IPAddress[]? SetNameServers(params IPAddress[] nameServers) { - if (this.options.Value.SetToLocalMachine && OperatingSystem.IsWindows()) + if (OperatingSystem.IsWindows()) { try { @@ -140,34 +136,12 @@ namespace FastGithub.Dns this.logger.LogWarning($"设置本机dns失败:{ex.Message}"); } } + else + { + this.logger.LogError("不支持自动为本机设备设置dns值"); + } return default; } - - private class CompositeRequestResolver : IRequestResolver - { - private readonly IRequestResolver upStreamResolver; - private readonly IRequestResolver[] customResolvers; - - public CompositeRequestResolver(IPAddress upStream, params IRequestResolver[] customResolvers) - { - this.upStreamResolver = new UdpRequestResolver(new IPEndPoint(upStream, 53)); - this.customResolvers = customResolvers; - } - - public async Task Resolve(IRequest request, CancellationToken cancellationToken = default) - { - foreach (IRequestResolver resolver in customResolvers) - { - var response = await resolver.Resolve(request, cancellationToken); - if (response.AnswerRecords.Count > 0) - { - return response; - } - } - - return await this.upStreamResolver.Resolve(request, cancellationToken); - } - } } } diff --git a/FastGithub.Dns/DnsServiceCollectionExtensions.cs b/FastGithub.Dns/DnsServiceCollectionExtensions.cs index 715480f..2ad2699 100644 --- a/FastGithub.Dns/DnsServiceCollectionExtensions.cs +++ b/FastGithub.Dns/DnsServiceCollectionExtensions.cs @@ -1,5 +1,4 @@ using FastGithub.Dns; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace FastGithub @@ -12,14 +11,12 @@ namespace FastGithub /// /// 注册github的dns服务 /// - /// - /// 配置 + /// /// - public static IServiceCollection AddGithubDns(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddGithubDns(this IServiceCollection services) { - var assembly = typeof(DnsServiceCollectionExtensions).Assembly; return services - .AddServiceAndOptions(assembly, configuration) + .AddSingleton() .AddHostedService(); } } diff --git a/FastGithub.Dns/FastGihubResolver.cs b/FastGithub.Dns/FastGihubResolver.cs new file mode 100644 index 0000000..0520069 --- /dev/null +++ b/FastGithub.Dns/FastGihubResolver.cs @@ -0,0 +1,70 @@ +using DNS.Client.RequestResolver; +using DNS.Protocol; +using DNS.Protocol.ResourceRecords; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.Dns +{ + /// + /// 反向代理解析器 + /// + sealed class FastGihubResolver : IRequestResolver + { + private readonly IRequestResolver untrustedDnsResolver; + private readonly IOptionsMonitor options; + private readonly ILogger logger; + + /// + /// github相关域名解析器 + /// + /// + /// + public FastGihubResolver( + IOptionsMonitor options, + ILogger logger) + { + this.options = options; + this.logger = logger; + this.untrustedDnsResolver = new UdpRequestResolver(options.CurrentValue.UntrustedDns.ToIPEndPoint()); + } + + /// + /// 解析域名 + /// + /// + /// + /// + public async Task Resolve(IRequest request, CancellationToken cancellationToken = default) + { + var response = Response.FromRequest(request); + if (request is not RemoteRequest remoteRequest) + { + return response; + } + + var question = request.Questions.FirstOrDefault(); + if (question == null || question.Type != RecordType.A) + { + return response; + } + + var domain = question.Name; + if (this.options.CurrentValue.IsMatch(domain.ToString()) == true) + { + var localAddress = remoteRequest.GetLocalAddress() ?? IPAddress.Loopback; + var record = new IPAddressResourceRecord(domain, localAddress, TimeSpan.FromMinutes(1d)); + this.logger.LogInformation($"[{domain}->{localAddress}]"); + response.AnswerRecords.Add(record); + return response; + } + + return await this.untrustedDnsResolver.Resolve(request, cancellationToken); + } + } +} diff --git a/FastGithub.Dns/FastGithub.Dns.csproj b/FastGithub.Dns/FastGithub.Dns.csproj index e2d2214..efd748e 100644 --- a/FastGithub.Dns/FastGithub.Dns.csproj +++ b/FastGithub.Dns/FastGithub.Dns.csproj @@ -6,6 +6,5 @@ - diff --git a/FastGithub.Dns/GithubRequestResolver.cs b/FastGithub.Dns/GithubRequestResolver.cs deleted file mode 100644 index 221f336..0000000 --- a/FastGithub.Dns/GithubRequestResolver.cs +++ /dev/null @@ -1,102 +0,0 @@ -using DNS.Client.RequestResolver; -using DNS.Protocol; -using DNS.Protocol.ResourceRecords; -using FastGithub.Scanner; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace FastGithub.Dns -{ - /// - /// github相关域名解析器 - /// - [Service(ServiceLifetime.Singleton)] - sealed class GithubRequestResolver : IRequestResolver - { - private readonly IGithubResolver githubResolver; - private readonly IOptionsMonitor options; - private readonly ILogger logger; - - /// - /// github相关域名解析器 - /// - /// - /// - /// - public GithubRequestResolver( - IGithubResolver githubResolver, - IOptionsMonitor options, - ILogger logger) - { - this.githubResolver = githubResolver; - this.options = options; - this.logger = logger; - } - - /// - /// 解析域名 - /// - /// - /// - /// - public Task Resolve(IRequest request, CancellationToken cancellationToken = default) - { - IResponse response = Response.FromRequest(request); - if (request is not RemoteRequest remoteRequest) - { - return Task.FromResult(response); - } - - var question = request.Questions.FirstOrDefault(); - if (question == null || question.Type != RecordType.A) - { - return Task.FromResult(response); - } - - var domain = question.Name; - if (this.githubResolver.IsSupported(domain.ToString()) == false) - { - return Task.FromResult(response); - } - - var record = this.GetAnswerRecord(remoteRequest, domain); - if (record != null) - { - this.logger.LogInformation($"[{domain}->{record.IPAddress}]"); - response.AnswerRecords.Add(record); - } - - return Task.FromResult(response); - } - - /// - /// 获取答案 - /// - /// - /// - /// - private IPAddressResourceRecord? GetAnswerRecord(RemoteRequest request, Domain domain) - { - if (this.options.CurrentValue.UseGithubReverseProxy == true) - { - var localAddress = request.GetLocalAddress() ?? IPAddress.Loopback; - return new IPAddressResourceRecord(domain, localAddress, TimeSpan.FromMinutes(1d)); - } - - var githubAddress = this.githubResolver.Resolve(domain.ToString()); - if (githubAddress == null) - { - return default; - } - - var ttl = this.options.CurrentValue.GithubTTL; - return new IPAddressResourceRecord(domain, githubAddress, ttl); - } - } -} diff --git a/FastGithub.ReverseProxy/FastGithub.ReverseProxy.csproj b/FastGithub.ReverseProxy/FastGithub.ReverseProxy.csproj index 818a7d8..15b19a6 100644 --- a/FastGithub.ReverseProxy/FastGithub.ReverseProxy.csproj +++ b/FastGithub.ReverseProxy/FastGithub.ReverseProxy.csproj @@ -3,15 +3,16 @@ net5.0 - + - + + - + - + diff --git a/FastGithub.ReverseProxy/GithubHttpClientHanlder.cs b/FastGithub.ReverseProxy/GithubHttpClientHanlder.cs new file mode 100644 index 0000000..2894191 --- /dev/null +++ b/FastGithub.ReverseProxy/GithubHttpClientHanlder.cs @@ -0,0 +1,84 @@ +using System; +using System.Net.Http; +using System.Net.Security; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.ReverseProxy +{ + /// + /// 适用于请求github的HttpClientHandler + /// + class GithubHttpClientHanlder : DelegatingHandler + { + private readonly GithubResolver githubResolver; + + /// + /// 请求github的HttpClientHandler + /// + /// + public GithubHttpClientHanlder(GithubResolver githubResolver) + { + this.githubResolver = githubResolver; + this.InnerHandler = CreateNoneSniHttpHandler(); + } + + /// + /// 创建无Sni发送的httpHandler + /// + /// + private static HttpMessageHandler CreateNoneSniHttpHandler() + { + return new SocketsHttpHandler + { + Proxy = null, + UseProxy = false, + AllowAutoRedirect = false, + ConnectCallback = async (ctx, ct) => + { + var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(ctx.DnsEndPoint, ct); + var stream = new NetworkStream(socket, ownsSocket: true); + if (ctx.InitialRequestMessage.Headers.Host == null) + { + return stream; + } + + var sslStream = new SslStream(stream, leaveInnerStreamOpen: false); + await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions + { + TargetHost = string.Empty, + RemoteCertificateValidationCallback = delegate { return true; } + }, ct); + return sslStream; + } + }; + } + + + /// + /// 替换github域名为ip + /// + /// + /// + /// + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var uri = request.RequestUri; + if (uri != null && uri.HostNameType == UriHostNameType.Dns) + { + var address = await this.githubResolver.ResolveAsync(uri.Host, cancellationToken); + var builder = new UriBuilder(uri) + { + Scheme = Uri.UriSchemeHttp, + Host = address.ToString(), + Port = 443 + }; + request.RequestUri = builder.Uri; + request.Headers.Host = uri.Host; + } + return await base.SendAsync(request, cancellationToken); + } + } +} diff --git a/FastGithub.ReverseProxy/GithubResolver.cs b/FastGithub.ReverseProxy/GithubResolver.cs new file mode 100644 index 0000000..e08474b --- /dev/null +++ b/FastGithub.ReverseProxy/GithubResolver.cs @@ -0,0 +1,64 @@ +using DNS.Client; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.ReverseProxy +{ + /// + /// github解析器 + /// + sealed class GithubResolver + { + private readonly IMemoryCache memoryCache; + private readonly IOptionsMonitor options; + private readonly ILogger logger; + + /// + /// github解析器 + /// + /// + public GithubResolver( + IMemoryCache memoryCache, + IOptionsMonitor options, + ILogger logger) + { + this.memoryCache = memoryCache; + this.options = options; + this.logger = logger; + } + + /// + /// 解析指定的域名 + /// + /// + /// + public async Task ResolveAsync(string domain, CancellationToken cancellationToken) + { + // 缓存,避免做不必要的并发查询 + var key = $"domain:{domain}"; + var address = await this.memoryCache.GetOrCreateAsync(key, async e => + { + e.SetAbsoluteExpiration(TimeSpan.FromMinutes(2d)); + var dnsClient = new DnsClient(this.options.CurrentValue.TrustedDns.ToIPEndPoint()); + var addresses = await dnsClient.Lookup(domain, DNS.Protocol.RecordType.A, cancellationToken); + return addresses?.FirstOrDefault(); + }); + + if (address == null) + { + var message = $"无法解析{domain}的ip"; + this.logger.LogWarning(message); + throw new HttpRequestException(message); + } + this.logger.LogInformation($"[{domain}->{address}]"); + return address; + } + } +} diff --git a/FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs b/FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs index 28eb2f9..5b565e8 100644 --- a/FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs +++ b/FastGithub.ReverseProxy/ReverseProxyApplicationBuilderExtensions.cs @@ -1,7 +1,8 @@ -using FastGithub.Scanner; +using FastGithub.ReverseProxy; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using System.Net.Http; using Yarp.ReverseProxy.Forwarder; @@ -21,12 +22,12 @@ namespace FastGithub { var httpForwarder = app.ApplicationServices.GetRequiredService(); var httpClientHanlder = app.ApplicationServices.GetRequiredService(); - var githubResolver = app.ApplicationServices.GetRequiredService(); + var options = app.ApplicationServices.GetRequiredService>(); app.Use(next => async context => { var host = context.Request.Host.Host; - if (githubResolver.IsSupported(host) == false) + if (options.CurrentValue.IsMatch(host) == false) { await context.Response.WriteAsJsonAsync(new { message = $"不支持以{host}访问" }); } diff --git a/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs b/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs index df95e9e..e1e5a05 100644 --- a/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs +++ b/FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using FastGithub.ReverseProxy; using Microsoft.Extensions.DependencyInjection; namespace FastGithub @@ -11,15 +11,15 @@ namespace FastGithub /// /// gitub反向代理 /// - /// - /// + /// /// - public static IServiceCollection AddGithubReverseProxy(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddGithubReverseProxy(this IServiceCollection services) { - var assembly = typeof(ReverseProxyServiceCollectionExtensions).Assembly; return services - .AddServiceAndOptions(assembly, configuration) - .AddHttpForwarder(); + .AddMemoryCache() + .AddHttpForwarder() + .AddSingleton() + .AddTransient(); } } } diff --git a/FastGithub.sln b/FastGithub.sln index 6923f45..0c59484 100644 --- a/FastGithub.sln +++ b/FastGithub.sln @@ -9,11 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Core", "FastGith EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Dns", "FastGithub.Dns\FastGithub.Dns.csproj", "{43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Scanner", "FastGithub.Scanner\FastGithub.Scanner.csproj", "{7F24CD2F-07C0-4002-A534-80688DE95ECF}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Upgrade", "FastGithub.Upgrade\FastGithub.Upgrade.csproj", "{8239A077-A84C-4FDF-A204-02A2DE4243F3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastGithub.ReverseProxy", "FastGithub.ReverseProxy\FastGithub.ReverseProxy.csproj", "{28326D0F-B0FB-4B6B-A65A-C69ACB72CAD8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.ReverseProxy", "FastGithub.ReverseProxy\FastGithub.ReverseProxy.csproj", "{28326D0F-B0FB-4B6B-A65A-C69ACB72CAD8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,10 +31,6 @@ Global {43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}.Debug|Any CPU.Build.0 = Debug|Any CPU {43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}.Release|Any CPU.ActiveCfg = Release|Any CPU {43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}.Release|Any CPU.Build.0 = Release|Any CPU - {7F24CD2F-07C0-4002-A534-80688DE95ECF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F24CD2F-07C0-4002-A534-80688DE95ECF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F24CD2F-07C0-4002-A534-80688DE95ECF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F24CD2F-07C0-4002-A534-80688DE95ECF}.Release|Any CPU.Build.0 = Release|Any CPU {8239A077-A84C-4FDF-A204-02A2DE4243F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8239A077-A84C-4FDF-A204-02A2DE4243F3}.Debug|Any CPU.Build.0 = Debug|Any CPU {8239A077-A84C-4FDF-A204-02A2DE4243F3}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/FastGithub/Program.cs b/FastGithub/Program.cs index fa4ece1..e827f73 100644 --- a/FastGithub/Program.cs +++ b/FastGithub/Program.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace FastGithub @@ -30,16 +30,14 @@ namespace FastGithub { c.ValidateOnBuild = false; }) - .ConfigureAppConfiguration(c => - { - c.AddJsonFile("appsettings.github.json", optional: true); - }) .ConfigureServices((ctx, services) => { services.AddAppUpgrade(); - services.AddGithubDns(ctx.Configuration); - services.AddGithubReverseProxy(ctx.Configuration); - services.AddGithubScanner(ctx.Configuration); + services.AddGithubDns(); + services.AddGithubReverseProxy(); + services.AddOptions() + .Bind(ctx.Configuration.GetSection(nameof(FastGithub))) + .Validate(opt => opt.TrustedDns.Validate() && opt.UntrustedDns.Validate(), "无效的Dns配置"); }) .ConfigureWebHostDefaults(web => { diff --git a/FastGithub/appsettings.github.json b/FastGithub/appsettings.github.json deleted file mode 100644 index b3c21c9..0000000 --- a/FastGithub/appsettings.github.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "Lookup": { // ip - "Domains": [ // ҵgithubҪ - "github.com", - "api.github.com", - "collector.githubapp.com", - "github.githubassets.com", - "raw.githubusercontent.com", - "avatars.githubusercontent.com", - "favicons.githubusercontent.com" - ] - }, - "Scan": { - "HttpsScan": { - "Rules": { // ɨȱʧĬHEADĸ· - "github.com": { - "Method": "HEAD", - "Path": "/xljiulang/FastGithub" - }, - "github.githubassets.com": { - "Method": "HEAD", - "Path": "/favicons/favicon.png" - }, - "raw.githubusercontent.com": { - "Method": "HEAD", - "Path": "/xljiulang/FastGithub/master/README.md" - }, - "avatars.githubusercontent.com": { - "Method": "HEAD", - "Path": "/u/8308014?s=40&v=4" - }, - "favicons.githubusercontent.com": { - "Method": "HEAD", - "Path": "/github.com" - } - } - } - } -} diff --git a/FastGithub/appsettings.json b/FastGithub/appsettings.json index 2d20684..8adf40c 100644 --- a/FastGithub/appsettings.json +++ b/FastGithub/appsettings.json @@ -1,43 +1,20 @@ { - "Dns": { - "UpStream": "114.114.114.114", // dns - "GithubTTL": "00:05:00", // githubĴʱ - "SetToLocalMachine": true, // Ƿñʹôdns(֧windows) - "UseGithubReverseProxy": true // Ƿʹ÷githubԽӱõ⣬ͻҪװFastGithub.cer֤ - }, - "Lookup": { // ip - "IPAddressComProvider": { - "Enable": true // Ǵôaddress.comip + "FastGithub": { + "TrustedDns": { + "Address": "127.0.0.1", + "Port": 5533 }, - "GithubMetaProvider": { - "Enable": true, // Ƿgithubȡipб - "MetaUri": "https://gitee.com/jiulang/fast-github/raw/master/FastGithub/meta.json" + "UnTrustedDns": { + "Address": "114.114.114.114", + "Port": 53 }, - "PublicDnsProvider": { - "Enable": true, // ǷҪdnsip - "Timeout": "00:00:00.200", // dnsѯʱʱ - "Dnss": [ // dnsб - "1.2.4.8", - "8.8.8.8", - "223.5.5.5", - "123.125.81.6", - "119.29.29.29", - "208.67.220.220", - "114.114.114.114" - ] - } - }, - "Scan": { - "FullScanInterval": "02:00:00", // ɨʱ - "ResultScanInterval": "00:01:00", // ɨʱ - "TcpScan": { - "Timeout": "00:00:01", // tcpɨ賬ʱʱ - "CacheExpiration": "00:30:00" // ɨʱ - }, - "HttpsScan": { - "Timeout": "00:00:05", // httpsɨ賬ʱʱ - "ConnectionClose": false // Ƿʹhttps - } + "DomainMatches": [ + "github.com", + "*.github.com", + "*.githubapp.com", + "*.githubassets.com", + "*.githubusercontent.com" + ] }, "Logging": { "LogLevel": {