diff --git a/FastGithub.Core/DomainConfig.cs b/FastGithub.Core/DomainConfig.cs index 345d1a7..bbde172 100644 --- a/FastGithub.Core/DomainConfig.cs +++ b/FastGithub.Core/DomainConfig.cs @@ -12,6 +12,11 @@ namespace FastGithub /// public bool TlsSni { get; init; } + /// + /// 自定义SNI值的表达式 + /// + public string? TlsSniPattern { get; init; } + /// /// 请求超时时长 /// @@ -27,5 +32,22 @@ namespace FastGithub /// 自定义响应 /// public ResponseConfig? Response { get; init; } + + /// + /// 获取TlsSniPattern + /// + /// + public TlsSniPattern GetTlsSniPattern() + { + if (this.TlsSni == false) + { + return FastGithub.TlsSniPattern.None; + } + if (string.IsNullOrEmpty(this.TlsSniPattern)) + { + return FastGithub.TlsSniPattern.Domain; + } + return new TlsSniPattern(this.TlsSniPattern); + } } } diff --git a/FastGithub.Core/TlsSniPattern.cs b/FastGithub.Core/TlsSniPattern.cs new file mode 100644 index 0000000..f1af0f9 --- /dev/null +++ b/FastGithub.Core/TlsSniPattern.cs @@ -0,0 +1,86 @@ +using System; +using System.Net; + +namespace FastGithub +{ + /// + /// Sni自定义值表达式 + /// @domain变量表示取域名值 + /// @ipadress变量表示取ip + /// @random变量表示取随机值 + /// + public struct TlsSniPattern + { + /// + /// 获取表示式值 + /// + public string Value { get; } + + /// + /// 无SNI + /// + public static TlsSniPattern None { get; } = new TlsSniPattern(string.Empty); + + /// + /// 域名SNI + /// + public static TlsSniPattern Domain { get; } = new TlsSniPattern("@domain"); + + /// + /// IP值的SNI + /// + public static TlsSniPattern IPAddress { get; } = new TlsSniPattern("@ipaddress"); + + /// + /// 随机值的SNI + /// + public static TlsSniPattern Random { get; } = new TlsSniPattern("@random"); + + /// + /// Sni自定义值表达式 + /// + /// 表示式值 + public TlsSniPattern(string value) + { + this.Value = value; + } + + /// + /// 更新域名 + /// + /// + public TlsSniPattern WithDomain(string domain) + { + var value = this.Value.Replace(Domain.Value, domain, StringComparison.OrdinalIgnoreCase); + return new TlsSniPattern(value); + } + + /// + /// 更新ip地址 + /// + /// + public TlsSniPattern WithIPAddress(IPAddress address) + { + var value = this.Value.Replace(IPAddress.Value, address.ToString(), StringComparison.OrdinalIgnoreCase); + return new TlsSniPattern(value); + } + + /// + /// 更新随机数 + /// + public TlsSniPattern WithRandom() + { + var value = this.Value.Replace(Random.Value, Environment.TickCount64.ToString(), StringComparison.OrdinalIgnoreCase); + return new TlsSniPattern(value); + } + + /// + /// 转换为文本 + /// + /// + public override string ToString() + { + return this.Value; + } + } +} diff --git a/FastGithub.ReverseProxy/HttpClient.cs b/FastGithub.ReverseProxy/HttpClient.cs index fedb9ae..a8c4887 100644 --- a/FastGithub.ReverseProxy/HttpClient.cs +++ b/FastGithub.ReverseProxy/HttpClient.cs @@ -10,18 +10,18 @@ namespace FastGithub.ReverseProxy /// class HttpClient : HttpMessageInvoker { - private readonly string tlsSniValue; + private readonly TlsSniPattern tlsSniPattern; /// /// YARP的HttpClient /// /// - /// + /// /// - public HttpClient(HttpMessageHandler handler, string tlsSniValue, bool disposeHandler = false) : + public HttpClient(HttpMessageHandler handler, TlsSniPattern tlsSniPattern, bool disposeHandler = false) : base(handler, disposeHandler) { - this.tlsSniValue = tlsSniValue; + this.tlsSniPattern = tlsSniPattern; } /// @@ -33,7 +33,7 @@ namespace FastGithub.ReverseProxy public override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var isHttps = request.RequestUri?.Scheme == Uri.UriSchemeHttps; - request.SetSniContext(new SniContext(isHttps, this.tlsSniValue)); + request.SetTlsSniContext(new TlsSniContext(isHttps, this.tlsSniPattern)); return base.SendAsync(request, cancellationToken); } } diff --git a/FastGithub.ReverseProxy/HttpClientHanlder.cs b/FastGithub.ReverseProxy/HttpClientHanlder.cs index 7063332..2e2c18a 100644 --- a/FastGithub.ReverseProxy/HttpClientHanlder.cs +++ b/FastGithub.ReverseProxy/HttpClientHanlder.cs @@ -35,13 +35,14 @@ namespace FastGithub.ReverseProxy Proxy = null, UseProxy = false, AllowAutoRedirect = false, - ConnectCallback = async (ctx, ct) => + ConnectCallback = async (context, cancellationToken) => { var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(ctx.DnsEndPoint, ct); + await socket.ConnectAsync(context.DnsEndPoint, cancellationToken); var stream = new NetworkStream(socket, ownsSocket: true); - var sniContext = ctx.InitialRequestMessage.GetSniContext(); - if (sniContext.IsHttps == false) + + var tlsSniContext = context.InitialRequestMessage.GetTlsSniContext(); + if (tlsSniContext.IsHttps == false) { return stream; } @@ -49,9 +50,9 @@ namespace FastGithub.ReverseProxy var sslStream = new SslStream(stream, leaveInnerStreamOpen: false); await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions { - TargetHost = sniContext.TlsSniValue, + TargetHost = tlsSniContext.TlsSniPattern.Value, RemoteCertificateValidationCallback = delegate { return true; } - }, ct); + }, cancellationToken); return sslStream; } }; @@ -76,6 +77,9 @@ namespace FastGithub.ReverseProxy }; request.RequestUri = builder.Uri; request.Headers.Host = uri.Host; + + var context = request.GetTlsSniContext(); + context.TlsSniPattern = context.TlsSniPattern.WithDomain(uri.Host).WithIPAddress(address).WithRandom(); } return await base.SendAsync(request, cancellationToken); } diff --git a/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs b/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs index c92edb5..dec5eb4 100644 --- a/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs +++ b/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs @@ -56,10 +56,10 @@ namespace FastGithub.ReverseProxy var destinationPrefix = GetDestinationPrefix(host, domainConfig.Destination); var requestConfig = new ForwarderRequestConfig { Timeout = domainConfig.Timeout }; - var tlsSniValue = domainConfig.TlsSni ? destinationPrefix.Host : string.Empty; - using var httpClient = new HttpClient(this.httpClientHanlder, tlsSniValue); + var tlsSniPattern = domainConfig.GetTlsSniPattern(); + using var httpClient = new HttpClient(this.httpClientHanlder, tlsSniPattern); - var error = await httpForwarder.SendAsync(context, destinationPrefix.ToString(), httpClient, requestConfig); + var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, requestConfig); await ResponseErrorAsync(context, error); } } @@ -70,15 +70,16 @@ namespace FastGithub.ReverseProxy /// /// /// - private Uri GetDestinationPrefix(string host, Uri? destination) + private string GetDestinationPrefix(string host, Uri? destination) { - var defaultValue = new Uri($"https://{host}/"); + var defaultValue = $"https://{host}/"; if (destination == null) { return defaultValue; } - var result = new Uri(defaultValue, destination); + var baseUri = new Uri(defaultValue); + var result = new Uri(baseUri, destination).ToString(); this.logger.LogInformation($"[{defaultValue}->{result}]"); return result; } diff --git a/FastGithub.ReverseProxy/SniContext.cs b/FastGithub.ReverseProxy/TlsSniContext.cs similarity index 59% rename from FastGithub.ReverseProxy/SniContext.cs rename to FastGithub.ReverseProxy/TlsSniContext.cs index 5c33d02..0e308d5 100644 --- a/FastGithub.ReverseProxy/SniContext.cs +++ b/FastGithub.ReverseProxy/TlsSniContext.cs @@ -3,7 +3,7 @@ /// /// Sni上下文 /// - sealed class SniContext + sealed class TlsSniContext { /// /// 获取是否为https请求 @@ -11,19 +11,19 @@ public bool IsHttps { get; } /// - /// 获取Sni值 + /// 获取或设置Sni值的表达式 /// - public string TlsSniValue { get; } + public TlsSniPattern TlsSniPattern { get; set; } /// /// Sni上下文 /// /// - /// - public SniContext(bool isHttps, string tlsSniValue) + /// + public TlsSniContext(bool isHttps, TlsSniPattern tlsSniPattern) { this.IsHttps = isHttps; - this.TlsSniValue = tlsSniValue; + this.TlsSniPattern = tlsSniPattern; } } } diff --git a/FastGithub.ReverseProxy/SniContextExtensions.cs b/FastGithub.ReverseProxy/TlsSniContextExtensions.cs similarity index 61% rename from FastGithub.ReverseProxy/SniContextExtensions.cs rename to FastGithub.ReverseProxy/TlsSniContextExtensions.cs index da9cb3d..0d44351 100644 --- a/FastGithub.ReverseProxy/SniContextExtensions.cs +++ b/FastGithub.ReverseProxy/TlsSniContextExtensions.cs @@ -6,32 +6,32 @@ namespace FastGithub.ReverseProxy /// /// SniContext扩展 /// - static class SniContextExtensions + static class TlsSniContextExtensions { - private static readonly HttpRequestOptionsKey key = new(nameof(SniContext)); + private static readonly HttpRequestOptionsKey key = new(nameof(TlsSniContext)); /// - /// 设置SniContext + /// 设置TlsSniContext /// /// /// - public static void SetSniContext(this HttpRequestMessage httpRequestMessage, SniContext context) + public static void SetTlsSniContext(this HttpRequestMessage httpRequestMessage, TlsSniContext context) { httpRequestMessage.Options.Set(key, context); } /// - /// 获取SniContext + /// 获取TlsSniContext /// /// /// - public static SniContext GetSniContext(this HttpRequestMessage httpRequestMessage) + public static TlsSniContext GetTlsSniContext(this HttpRequestMessage httpRequestMessage) { if (httpRequestMessage.Options.TryGetValue(key, out var value)) { return value; } - throw new InvalidOperationException($"请先调用{nameof(SetSniContext)}"); + throw new InvalidOperationException($"请先调用{nameof(SetTlsSniContext)}"); } } } diff --git a/FastGithub/appsettings.json b/FastGithub/appsettings.json index 03c8323..696de65 100644 --- a/FastGithub/appsettings.json +++ b/FastGithub/appsettings.json @@ -11,6 +11,7 @@ "DomainConfigs": { // 域名的*表示0到多个任意字符 "github.com": { "TlsSni": false, // 指示tls握手时是否发送SNI + "TlsSniPattern": null, // SNI表达式,@domain变量表示取域名值 @ipadress变量表示取ip @random变量表示取随机值,其它字符保留不替换 "Timeout": null, // 请求超时时长,格式为"00:02:00",默认为null "Destination": null // 请求目的地,格式为绝对或相对Uri,默认null },