增加域名配置
This commit is contained in:
parent
6559644c99
commit
68c5791323
@ -18,7 +18,7 @@ namespace FastGithub
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
public int Port { get; set; } = 53;
|
||||
|
||||
/// <summary>
|
||||
/// 转换为IPEndPoint
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
namespace FastGithub
|
||||
using System;
|
||||
|
||||
namespace FastGithub
|
||||
{
|
||||
/// <summary>
|
||||
/// 域名配置
|
||||
@ -12,9 +14,8 @@
|
||||
|
||||
/// <summary>
|
||||
/// 目的地
|
||||
/// 支持ip或域名
|
||||
/// 留空则本域名
|
||||
/// 格式为相对或绝对uri
|
||||
/// </summary>
|
||||
public string? Destination { get; set; }
|
||||
public Uri? Destination { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
63
FastGithub.Core/FastGithubConfig.cs
Normal file
63
FastGithub.Core/FastGithubConfig.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace FastGithub
|
||||
{
|
||||
/// <summary>
|
||||
/// FastGithub配置
|
||||
/// </summary>
|
||||
public class FastGithubConfig
|
||||
{
|
||||
private readonly Dictionary<DomainMatch, DomainConfig> domainConfigs;
|
||||
|
||||
/// <summary>
|
||||
/// 获取信任dns
|
||||
/// </summary>
|
||||
public IPEndPoint TrustedDns { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取非信任dns
|
||||
/// </summary>
|
||||
public IPEndPoint UnTrustedDns { get; }
|
||||
|
||||
/// <summary>
|
||||
/// FastGithub配置
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
public FastGithubConfig(FastGithubOptions options)
|
||||
{
|
||||
this.TrustedDns = options.TrustedDns.ToIPEndPoint();
|
||||
this.UnTrustedDns = options.UntrustedDns.ToIPEndPoint();
|
||||
this.domainConfigs = options.DomainConfigs.ToDictionary(kv => new DomainMatch(kv.Key), kv => kv.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否匹配指定的域名
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsMatch(string domain)
|
||||
{
|
||||
return this.domainConfigs.Keys.Any(item => item.IsMatch(domain));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取域名配置
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
/// <param name="domainConfig"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryGetDomainConfig(string domain, [MaybeNullWhen(false)] out DomainConfig domainConfig)
|
||||
{
|
||||
var key = this.domainConfigs.Keys.FirstOrDefault(item => item.IsMatch(domain));
|
||||
if (key == null)
|
||||
{
|
||||
domainConfig = default;
|
||||
return false;
|
||||
}
|
||||
return this.domainConfigs.TryGetValue(key, out domainConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FastGithub
|
||||
{
|
||||
@ -10,13 +7,6 @@ namespace FastGithub
|
||||
/// </summary>
|
||||
public class FastGithubOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 域名
|
||||
/// </summary>
|
||||
private DomainMatch[]? domainMatches;
|
||||
private IPEndPoint? trustedDnsIPEndPoint;
|
||||
private IPEndPoint? unTrustedDnsIPEndPoint;
|
||||
|
||||
/// <summary>
|
||||
/// 受信任的dns服务
|
||||
/// </summary>
|
||||
@ -28,50 +18,29 @@ namespace FastGithub
|
||||
public DnsConfig UntrustedDns { get; set; } = new DnsConfig { IPAddress = "114.114.114.114", Port = 53 };
|
||||
|
||||
/// <summary>
|
||||
/// 代理的域名表达式
|
||||
/// 代理的域名配置
|
||||
/// </summary>
|
||||
public HashSet<string> DomainPatterns { get; set; } = new();
|
||||
public Dictionary<string, DomainConfig> DomainConfigs { get; set; } = new();
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 验证选项值
|
||||
/// 初始化选项为配置
|
||||
/// </summary>
|
||||
/// <exception cref="FastGithubException"></exception>
|
||||
public void Validate()
|
||||
public void InitConfig()
|
||||
{
|
||||
this.trustedDnsIPEndPoint = this.TrustedDns.ToIPEndPoint();
|
||||
this.unTrustedDnsIPEndPoint = this.UntrustedDns.ToIPEndPoint();
|
||||
this.domainMatches = this.DomainPatterns.Select(item => new DomainMatch(item)).ToArray();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 受信任的dns服务节点
|
||||
/// </summary>
|
||||
public IPEndPoint GetTrustedDns()
|
||||
{
|
||||
return this.trustedDnsIPEndPoint ?? throw new InvalidOperationException();
|
||||
this.fastGithubConfig = new FastGithubConfig(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 不受信任的dns服务节点
|
||||
/// 配置
|
||||
/// </summary>
|
||||
public IPEndPoint GetUnTrustedDns()
|
||||
{
|
||||
return this.unTrustedDnsIPEndPoint ?? throw new InvalidOperationException();
|
||||
}
|
||||
private FastGithubConfig? fastGithubConfig;
|
||||
|
||||
/// <summary>
|
||||
/// 是否匹配指定的域名
|
||||
/// 获取配置
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsMatch(string domain)
|
||||
{
|
||||
if (this.domainMatches == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return this.domainMatches.Any(item => item.IsMatch(domain));
|
||||
}
|
||||
public FastGithubConfig Config => this.fastGithubConfig!;
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ namespace FastGithub.Dns
|
||||
}
|
||||
|
||||
this.logger.LogInformation("dns服务启动成功");
|
||||
var secondary = IPAddress.Parse(options.CurrentValue.UntrustedDns.IPAddress);
|
||||
var secondary = options.CurrentValue.Config.UnTrustedDns.Address;
|
||||
this.dnsAddresses = this.SetNameServers(IPAddress.Loopback, secondary);
|
||||
FlushResolverCache();
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ namespace FastGithub.Dns
|
||||
{
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
this.untrustedResolver = new UdpRequestResolver(options.CurrentValue.GetTrustedDns());
|
||||
this.untrustedResolver = new UdpRequestResolver(options.CurrentValue.Config.TrustedDns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -56,7 +56,7 @@ namespace FastGithub.Dns
|
||||
}
|
||||
|
||||
var domain = question.Name;
|
||||
if (this.options.CurrentValue.IsMatch(domain.ToString()) == true)
|
||||
if (this.options.CurrentValue.Config.IsMatch(domain.ToString()) == true)
|
||||
{
|
||||
var localAddress = remoteEndPointRequest.GetLocalAddress() ?? IPAddress.Loopback;
|
||||
var record = new IPAddressResourceRecord(domain, localAddress, this.ttl);
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
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;
|
||||
|
||||
namespace FastGithub
|
||||
{
|
||||
@ -20,39 +16,8 @@ namespace FastGithub
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseHttpsReverseProxy(this IApplicationBuilder app)
|
||||
{
|
||||
var httpForwarder = app.ApplicationServices.GetRequiredService<IHttpForwarder>();
|
||||
var httpClientHanlder = app.ApplicationServices.GetRequiredService<NoSniHttpClientHanlder>();
|
||||
var options = app.ApplicationServices.GetRequiredService<IOptionsMonitor<FastGithubOptions>>();
|
||||
|
||||
app.Use(next => async context =>
|
||||
{
|
||||
var host = context.Request.Host.Host;
|
||||
if (options.CurrentValue.IsMatch(host) == false)
|
||||
{
|
||||
await context.Response.WriteAsJsonAsync(new { message = $"不支持以{host}访问" });
|
||||
return;
|
||||
}
|
||||
|
||||
var port = context.Request.Host.Port ?? 443;
|
||||
var destinationPrefix = $"https://{host}:{port}/";
|
||||
var httpClient = new HttpMessageInvoker(httpClientHanlder, disposeHandler: false);
|
||||
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
|
||||
|
||||
if (error != ForwarderError.None)
|
||||
{
|
||||
var errorFeature = context.GetForwarderErrorFeature();
|
||||
if (errorFeature != null)
|
||||
{
|
||||
await context.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
error = error.ToString(),
|
||||
message = errorFeature.Exception?.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
var middleware = app.ApplicationServices.GetRequiredService<ReverseProxyMiddleware>();
|
||||
return app.Use(next => context => middleware.InvokeAsync(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
103
FastGithub.ReverseProxy/ReverseProxyMiddleware.cs
Normal file
103
FastGithub.ReverseProxy/ReverseProxyMiddleware.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Yarp.ReverseProxy.Forwarder;
|
||||
|
||||
namespace FastGithub.ReverseProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// 反向代理中间件
|
||||
/// </summary>
|
||||
sealed class ReverseProxyMiddleware
|
||||
{
|
||||
private readonly IHttpForwarder httpForwarder;
|
||||
private readonly SniHttpClientHanlder sniHttpClientHanlder;
|
||||
private readonly NoSniHttpClientHanlder noSniHttpClientHanlder;
|
||||
private readonly IOptionsMonitor<FastGithubOptions> options;
|
||||
|
||||
public ReverseProxyMiddleware(
|
||||
IHttpForwarder httpForwarder,
|
||||
SniHttpClientHanlder sniHttpClientHanlder,
|
||||
NoSniHttpClientHanlder noSniHttpClientHanlder,
|
||||
IOptionsMonitor<FastGithubOptions> options)
|
||||
{
|
||||
this.httpForwarder = httpForwarder;
|
||||
this.sniHttpClientHanlder = sniHttpClientHanlder;
|
||||
this.noSniHttpClientHanlder = noSniHttpClientHanlder;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理请求
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
var host = context.Request.Host.Host;
|
||||
if (this.options.CurrentValue.Config.TryGetDomainConfig(host, out var domainConfig) == false)
|
||||
{
|
||||
await context.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
error = ForwarderError.NoAvailableDestinations.ToString(),
|
||||
message = $"不支持https反向代理{host}这个域名"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var destinationPrefix = GetDestinationPrefix(host, domainConfig.Destination);
|
||||
var httpClient = domainConfig.NoSni
|
||||
? new HttpMessageInvoker(this.noSniHttpClientHanlder, disposeHandler: false)
|
||||
: new HttpMessageInvoker(this.sniHttpClientHanlder, disposeHandler: false);
|
||||
|
||||
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
|
||||
await ResponseErrorAsync(context, error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取目标前缀
|
||||
/// </summary>
|
||||
/// <param name="host"></param>
|
||||
/// <param name="destination"></param>
|
||||
/// <returns></returns>
|
||||
private static string GetDestinationPrefix(string host, Uri? destination)
|
||||
{
|
||||
var defaultValue = $"https://{host}/";
|
||||
if (destination == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
var baseUri = new Uri(defaultValue);
|
||||
return new Uri(baseUri, destination).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入错误信息
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="error"></param>
|
||||
/// <returns></returns>
|
||||
private static async Task ResponseErrorAsync(HttpContext context, ForwarderError error)
|
||||
{
|
||||
if (error == ForwarderError.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var errorFeature = context.GetForwarderErrorFeature();
|
||||
if (errorFeature == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await context.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
error = error.ToString(),
|
||||
message = errorFeature.Exception?.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,9 @@ namespace FastGithub
|
||||
.AddMemoryCache()
|
||||
.AddHttpForwarder()
|
||||
.AddSingleton<DomainResolver>()
|
||||
.AddTransient<NoSniHttpClientHanlder>();
|
||||
.AddTransient<SniHttpClientHanlder>()
|
||||
.AddTransient<NoSniHttpClientHanlder>()
|
||||
.AddSingleton<ReverseProxyMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
FastGithub.ReverseProxy/SniHttpClientHanlder.cs
Normal file
60
FastGithub.ReverseProxy/SniHttpClientHanlder.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.ReverseProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// 携带Sni的的HttpClientHandler
|
||||
/// </summary>
|
||||
class SniHttpClientHanlder : DelegatingHandler
|
||||
{
|
||||
private readonly DomainResolver domainResolver;
|
||||
private readonly ILogger<NoSniHttpClientHanlder> logger;
|
||||
|
||||
/// <summary>
|
||||
/// 携带Sni的HttpClientHandler
|
||||
/// </summary>
|
||||
/// <param name="domainResolver"></param>
|
||||
public SniHttpClientHanlder(
|
||||
DomainResolver domainResolver,
|
||||
ILogger<NoSniHttpClientHanlder> logger)
|
||||
{
|
||||
this.domainResolver = domainResolver;
|
||||
this.logger = logger;
|
||||
|
||||
this.InnerHandler = new SocketsHttpHandler
|
||||
{
|
||||
Proxy = null,
|
||||
UseProxy = false,
|
||||
AllowAutoRedirect = false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 替换域名为ip
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var uri = request.RequestUri;
|
||||
if (uri != null && uri.HostNameType == UriHostNameType.Dns)
|
||||
{
|
||||
var address = await this.domainResolver.ResolveAsync(uri.Host, cancellationToken);
|
||||
this.logger.LogInformation($"[{address}--Sni->{uri.Host}]");
|
||||
|
||||
var builder = new UriBuilder(uri)
|
||||
{
|
||||
Host = address.ToString()
|
||||
};
|
||||
request.RequestUri = builder.Uri;
|
||||
request.Headers.Host = uri.Host;
|
||||
}
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -39,7 +39,7 @@ namespace FastGithub
|
||||
.AddDnscryptProxy()
|
||||
.AddOptions<FastGithubOptions>()
|
||||
.Bind(ctx.Configuration.GetSection(nameof(FastGithub)))
|
||||
.PostConfigure(opt => opt.Validate());
|
||||
.PostConfigure(opt => opt.InitConfig());
|
||||
})
|
||||
.ConfigureWebHostDefaults(web =>
|
||||
{
|
||||
|
||||
@ -1,23 +1,71 @@
|
||||
{
|
||||
"FastGithub": {
|
||||
"TrustedDns": { // 用于解析反向代理的域名,解析准确
|
||||
"TrustedDns": { // 用于解析DomainConfigs的域名,解析准确
|
||||
"IPAddress": "127.0.0.1",
|
||||
"Port": 5533 // 5533Ö¸Ïòdnscrypt-proxy
|
||||
},
|
||||
"UnTrustedDns": { // 用于解析用不代理的域名,解析速度快
|
||||
"UnTrustedDns": { // 用于解析不在DomainConfigs的域名,解析速度快
|
||||
"IPAddress": "114.114.114.114",
|
||||
"Port": 53
|
||||
},
|
||||
"DomainPatterns": [ // *表示0到多个任意字符
|
||||
"github.com",
|
||||
"githubstatus.com",
|
||||
"*.github.com",
|
||||
"*.github.io",
|
||||
"*.githubapp.com",
|
||||
"*.githubassets.com",
|
||||
"*.githubusercontent.com",
|
||||
"*github*.s3.amazonaws.com"
|
||||
]
|
||||
"DomainConfigs": { // 域名的*表示0到多个任意字符
|
||||
"github.com": {
|
||||
"NoSni": true,
|
||||
"Destination": null
|
||||
},
|
||||
"githubstatus.com": {
|
||||
"NoSni": true,
|
||||
"Destination": null
|
||||
},
|
||||
"*.github.com": {
|
||||
"NoSni": true,
|
||||
"Destination": null
|
||||
},
|
||||
"*.github.io": {
|
||||
"NoSni": true,
|
||||
"Destination": null
|
||||
},
|
||||
"*.githubapp.com": {
|
||||
"NoSni": true,
|
||||
"Destination": null
|
||||
},
|
||||
"*.githubassets.com": {
|
||||
"NoSni": true,
|
||||
"Destination": null
|
||||
},
|
||||
"*.githubusercontent.com": {
|
||||
"NoSni": true,
|
||||
"Destination": null
|
||||
},
|
||||
"*github*.s3.amazonaws.com": {
|
||||
"NoSni": true,
|
||||
"Destination": null
|
||||
},
|
||||
"ajax.googleapis.com": {
|
||||
"NoSni": false,
|
||||
"Destination": "https://gapis.geekzu.org/ajax/"
|
||||
},
|
||||
"fonts.googleapis.com": {
|
||||
"NoSni": false,
|
||||
"Destination": "https://fonts.geekzu.org/"
|
||||
},
|
||||
"themes.googleusercontent.com": {
|
||||
"NoSni": false,
|
||||
"Destination": "https://gapis.geekzu.org/g-themes/"
|
||||
},
|
||||
"fonts.gstatic.com": {
|
||||
"NoSni": false,
|
||||
"Destination": "https://gapis.geekzu.org/g-fonts/"
|
||||
},
|
||||
"secure.gravatar.com": {
|
||||
"NoSni": false,
|
||||
"Destination": "https://sdn.geekzu.org/"
|
||||
},
|
||||
"*.gravatar.com": {
|
||||
"NoSni": false,
|
||||
"Destination": "https://fdn.geekzu.org/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user