完善正向代理和反向代理共存

This commit is contained in:
老九 2021-09-14 20:30:14 +08:00
parent 9c2774dfa4
commit bc7a0b7084
14 changed files with 111 additions and 153 deletions

View File

@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>1.2.1</Version>
<Version>2.0.0</Version>
<Nullable>enable</Nullable>
<Description>github加速神器</Description>
<Copyright>https://github.com/dotnetcore/FastGithub</Copyright>

View File

@ -58,27 +58,40 @@ namespace FastGithub.Configuration
public static bool ContainsIPAddress(IPAddress address)
{
return GetAllIPAddresses().Contains(address);
}
}
/// <summary>
/// 获取与远程节点通讯的的本机IP地址
/// 获取可用的随机Tcp端口
/// </summary>
/// <param name="remoteEndPoint">远程地址</param>
/// <param name="addressFamily"></param>
/// <param name="min">最小值</param>
/// <returns></returns>
public static IPAddress? GetLocalIPAddress(EndPoint remoteEndPoint)
public static int GetAvailableTcpPort(AddressFamily addressFamily, int min = 1025)
{
try
var hashSet = new HashSet<int>();
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
foreach (var item in tcpListeners)
{
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Connect(remoteEndPoint);
return socket.LocalEndPoint is IPEndPoint localEndPoint ? localEndPoint.Address : default;
if (item.AddressFamily == addressFamily)
{
hashSet.Add(item.Port);
}
}
catch (Exception)
for (var port = min; port < ushort.MaxValue; port++)
{
return default;
if (hashSet.Contains(port) == false)
{
return port;
}
}
throw new FastGithubException("当前无可用的端口");
}
/// <summary>
/// 获取可用的随机端口
/// </summary>
@ -128,16 +141,5 @@ namespace FastGithub.Configuration
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
return tcpListeners.Any(item => item.Port == port) == false;
}
/// <summary>
/// 是否可以监听指定udp端口
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public static bool CanListenUdp(int port)
{
var udpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners();
return udpListeners.Any(item => item.Port == port) == false;
}
}
}

View File

@ -1,5 +1,4 @@
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;
@ -8,24 +7,24 @@ using System.Threading.Tasks;
namespace FastGithub.Dns
{
/// <summary>
/// dns投毒后台服务
/// dns拦截后台服务
/// </summary>
[SupportedOSPlatform("windows")]
sealed class DnsDnsPoisoningHostedService : BackgroundService
sealed class DnsInterceptHostedService : BackgroundService
{
private readonly DnsPoisoningServer dnsPoisoningServer;
private readonly DnsInterceptor dnsInterceptor;
private readonly IEnumerable<IConflictValidator> conflictValidators;
/// <summary>
/// dns后台服务
/// dns拦截后台服务
/// </summary>
/// <param name="dnsPoisoningServer"></param>
/// <param name="dnsInterceptor"></param>
/// <param name="conflictValidators"></param>
public DnsDnsPoisoningHostedService(
DnsPoisoningServer dnsPoisoningServer,
public DnsInterceptHostedService(
DnsInterceptor dnsInterceptor,
IEnumerable<IConflictValidator> conflictValidators)
{
this.dnsPoisoningServer = dnsPoisoningServer;
this.dnsInterceptor = dnsInterceptor;
this.conflictValidators = conflictValidators;
}
@ -37,15 +36,11 @@ namespace FastGithub.Dns
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield();
if (OperatingSystem.IsWindows())
foreach (var item in this.conflictValidators)
{
foreach (var item in this.conflictValidators)
{
await item.ValidateAsync();
}
this.dnsPoisoningServer.DnsPoisoning(stoppingToken);
await item.ValidateAsync();
}
this.dnsInterceptor.Intercept(stoppingToken);
}
}
}

View File

@ -14,14 +14,14 @@ using WinDivertSharp;
namespace FastGithub.Dns
{
/// <summary>
/// dns投毒服务
/// dns拦截器
/// </summary>
[SupportedOSPlatform("windows")]
sealed class DnsPoisoningServer
sealed class DnsInterceptor
{
const string DNS_FILTER = "udp.DstPort == 53";
private readonly FastGithubConfig fastGithubConfig;
private readonly ILogger<DnsPoisoningServer> logger;
private readonly ILogger<DnsInterceptor> logger;
private readonly TimeSpan ttl = TimeSpan.FromSeconds(10d);
/// <summary>
@ -35,19 +35,19 @@ namespace FastGithub.Dns
/// </summary>
/// <param name="fastGithubConfig"></param>
/// <param name="logger"></param>
public DnsPoisoningServer(
public DnsInterceptor(
FastGithubConfig fastGithubConfig,
ILogger<DnsPoisoningServer> logger)
ILogger<DnsInterceptor> logger)
{
this.fastGithubConfig = fastGithubConfig;
this.logger = logger;
}
/// <summary>
/// DNS投毒
/// DNS拦截
/// </summary>
/// <param name="cancellationToken"></param>
public void DnsPoisoning(CancellationToken cancellationToken)
public void Intercept(CancellationToken cancellationToken)
{
var handle = WinDivert.WinDivertOpen(DNS_FILTER, WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
if (handle == IntPtr.Zero)

View File

@ -11,17 +11,17 @@ namespace FastGithub
public static class ServiceCollectionExtensions
{
/// <summary>
/// 注册dns投毒服务
/// 注册dns拦截器
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[SupportedOSPlatform("windows")]
public static IServiceCollection AddDnsPoisoning(this IServiceCollection services)
{
services.TryAddSingleton<DnsPoisoningServer>();
public static IServiceCollection AddDnsInterceptor(this IServiceCollection services)
{
services.TryAddSingleton<DnsInterceptor>();
services.AddSingleton<IConflictValidator, HostsConflictValidator>();
services.AddSingleton<IConflictValidator, ProxyConflictValidtor>();
return services.AddHostedService<DnsDnsPoisoningHostedService>();
return services.AddHostedService<DnsInterceptHostedService>();
}
}
}

View File

@ -37,7 +37,7 @@ namespace FastGithub.DomainResolve
try
{
await this.dnscryptProxy.StartAsync(cancellationToken);
this.logger.LogInformation($"已监听端口{this.dnscryptProxy.LocalEndPoint?.Port}{this.dnscryptProxy}启动完成");
this.logger.LogInformation($"已监听dns://{this.dnscryptProxy.LocalEndPoint}{this.dnscryptProxy}启动完成");
}
catch (Exception ex)
{

View File

@ -17,7 +17,7 @@ namespace FastGithub
public static IApplicationBuilder UseHttpProxy(this IApplicationBuilder app)
{
var middleware = app.ApplicationServices.GetRequiredService<HttpProxyMiddleware>();
return app.Use(next => context => middleware.InvokeAsync(context, next));
return app.Use(next => context => middleware.InvokeAsync(context));
}
/// <summary>

View File

@ -20,7 +20,6 @@ namespace FastGithub.ReverseProxy
private readonly FastGithubConfig fastGithubConfig;
private readonly IDomainResolver domainResolver;
private readonly IHttpForwarder httpForwarder;
private readonly PortService portService;
private readonly SocketsHttpHandler socketsHttpHandler = new() { UseCookies = false, UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None };
/// <summary>
@ -29,26 +28,22 @@ namespace FastGithub.ReverseProxy
/// <param name="fastGithubConfig"></param>
/// <param name="domainResolver"></param>
/// <param name="httpForwarder"></param>
/// <param name="portService"></param>
public HttpProxyMiddleware(
FastGithubConfig fastGithubConfig,
IDomainResolver domainResolver,
IHttpForwarder httpForwarder,
PortService portService)
IHttpForwarder httpForwarder)
{
this.fastGithubConfig = fastGithubConfig;
this.domainResolver = domainResolver;
this.httpForwarder = httpForwarder;
this.portService = portService;
}
/// <summary>
/// 处理请求
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Method != HttpMethods.Connect)
{
@ -77,7 +72,6 @@ namespace FastGithub.ReverseProxy
}
}
/// <summary>
/// 获取目标终节点
/// </summary>
@ -85,28 +79,28 @@ namespace FastGithub.ReverseProxy
/// <returns></returns>
private async Task<EndPoint> GetTargetEndPointAsync(HttpRequest request)
{
var domain = request.Host.Host;
var port = request.Host.Port ?? 443;
var targetHost = request.Host.Host;
var targetPort = request.Host.Port ?? 443;
if (IPAddress.TryParse(domain, out var address) == true)
if (IPAddress.TryParse(targetHost, out var address) == true)
{
return new IPEndPoint(address, port);
return new IPEndPoint(address, targetPort);
}
if (this.fastGithubConfig.TryGetDomainConfig(domain, out _) == false)
if (this.fastGithubConfig.TryGetDomainConfig(targetHost, out _) == false)
{
return new DnsEndPoint(domain, port);
return new DnsEndPoint(targetHost, targetPort);
}
// https走反向代理中间人
if (port == 443)
if (targetPort == 443)
{
return new IPEndPoint(IPAddress.Loopback, this.portService.HttpsReverseProxyPort);
return new IPEndPoint(IPAddress.Loopback, HttpsReverseProxyPort.Value);
}
// dns优选
address = await this.domainResolver.ResolveAsync(new DnsEndPoint(domain, port));
return new IPEndPoint(address, port);
address = await this.domainResolver.ResolveAsync(new DnsEndPoint(targetHost, targetPort));
return new IPEndPoint(address, targetPort);
}
}
}

View File

@ -0,0 +1,17 @@
using FastGithub.Configuration;
using System;
using System.Net.Sockets;
namespace FastGithub.ReverseProxy
{
/// <summary>
/// https反向代理端口
/// </summary>
static class HttpsReverseProxyPort
{
/// <summary>
/// 获取端口值
/// </summary>
public static int Value { get; } = OperatingSystem.IsWindows() ? 443 : LocalMachine.GetAvailableTcpPort(AddressFamily.InterNetwork);
}
}

View File

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Net;
@ -30,18 +31,18 @@ namespace FastGithub
/// <param name="kestrel"></param>
public static void ListenHttpProxy(this KestrelServerOptions kestrel)
{
var httpPort = kestrel.ApplicationServices.GetRequiredService<PortService>().HttpProxyPort;
if (LocalMachine.CanListenTcp(httpPort) == false)
var httpProxyPort = kestrel.ApplicationServices.GetRequiredService<IOptions<FastGithubOptions>>().Value.HttpProxyPort;
if (LocalMachine.CanListenTcp(httpProxyPort) == false)
{
throw new FastGithubException("tcp端口{httpsPort}已经被其它进程占用,请在配置文件更换一个端口");
throw new FastGithubException($"tcp端口{httpProxyPort}已经被其它进程占用,请在配置文件更换{nameof(FastGithubOptions.HttpProxyPort)}为其它端口");
}
kestrel.Listen(IPAddress.Any, httpPort);
kestrel.GetLogger().LogInformation($"已监听tcp端口{httpPort}http代理启动完成");
kestrel.Listen(IPAddress.Loopback, httpProxyPort);
kestrel.GetLogger().LogInformation($"已监听http://127.0.0.1:{httpProxyPort}http代理启动完成");
}
/// <summary>
/// 监听ssh反向代理
/// 尝试监听ssh反向代理
/// </summary>
/// <param name="kestrel"></param>
public static void ListenSshReverseProxy(this KestrelServerOptions kestrel)
@ -49,13 +50,13 @@ namespace FastGithub
const int SSH_PORT = 22;
if (LocalMachine.CanListenTcp(SSH_PORT) == true)
{
kestrel.Listen(IPAddress.Any, SSH_PORT, listen => listen.UseConnectionHandler<SshReverseProxyHandler>());
kestrel.GetLogger().LogInformation($"已监听tcp端口{SSH_PORT}github的ssh代理启动完成");
kestrel.Listen(IPAddress.Loopback, SSH_PORT, listen => listen.UseConnectionHandler<SshReverseProxyHandler>());
kestrel.GetLogger().LogInformation($"已监听ssh://127.0.0.1:{SSH_PORT}ssh反向代理到github启动完成");
}
}
/// <summary>
/// 监听http反向代理
/// 尝试监听http反向代理
/// </summary>
/// <param name="kestrel"></param>
public static void ListenHttpReverseProxy(this KestrelServerOptions kestrel)
@ -63,8 +64,8 @@ namespace FastGithub
const int HTTP_PORT = 80;
if (LocalMachine.CanListenTcp(HTTP_PORT) == true)
{
kestrel.Listen(IPAddress.Any, HTTP_PORT);
kestrel.GetLogger().LogInformation($"已监听tcp端口{HTTP_PORT}http反向代理启动完成");
kestrel.Listen(IPAddress.Loopback, HTTP_PORT);
kestrel.GetLogger().LogInformation($"已监听http://127.0.0.1:{HTTP_PORT}http反向代理启动完成");
}
}
@ -73,9 +74,10 @@ namespace FastGithub
/// </summary>
/// <param name="kestrel"></param>
/// <exception cref="FastGithubException"></exception>
public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
/// <returns></returns>
public static int ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
{
var httpsPort = kestrel.ApplicationServices.GetRequiredService<PortService>().HttpsReverseProxyPort;
var httpsPort = HttpsReverseProxyPort.Value;
if (OperatingSystem.IsWindows())
{
TcpTable.KillPortOwner(httpsPort);
@ -90,15 +92,19 @@ namespace FastGithub
certService.CreateCaCertIfNotExists();
certService.InstallAndTrustCaCert();
kestrel.Listen(IPAddress.Any, httpsPort,
kestrel.Listen(IPAddress.Loopback, httpsPort,
listen => listen.UseHttps(https =>
https.ServerCertificateSelector = (ctx, domain) =>
certService.GetOrCreateServerCert(domain)));
var logger = kestrel.GetLogger();
logger.LogInformation($"已监听tcp端口{httpsPort}https反向代理启动完成");
}
if (httpsPort == 443)
{
var logger = kestrel.GetLogger();
logger.LogInformation($"已监听https://127.0.0.1:{httpsPort}https反向代理启动完成");
}
return httpsPort;
}
/// <summary>
/// 获取日志

View File

@ -1,48 +0,0 @@
using FastGithub.Configuration;
using Microsoft.Extensions.Options;
using System;
using System.Net.Sockets;
namespace FastGithub.ReverseProxy
{
/// <summary>
/// 端口管理服务
/// </summary>
public class PortService
{
private int httpsReverseProxyPort = -1;
/// <summary>
/// http代理端口
/// </summary>
public int HttpProxyPort { get; }
/// <summary>
/// 获取https反向代理端口
/// </summary>
public int HttpsReverseProxyPort
{
get
{
if (OperatingSystem.IsWindows())
{
return 443;
}
if (this.httpsReverseProxyPort < 0)
{
this.httpsReverseProxyPort = LocalMachine.GetAvailablePort(AddressFamily.InterNetwork);
}
return this.httpsReverseProxyPort;
}
}
/// <summary>
/// 端口管理服务
/// </summary>
/// <param name="options"></param>
public PortService(IOptions<FastGithubOptions> options)
{
this.HttpProxyPort = options.Value.HttpProxyPort;
}
}
}

View File

@ -4,12 +4,12 @@ using Microsoft.Extensions.DependencyInjection;
namespace FastGithub
{
/// <summary>
/// https反向代理的服务注册扩展
/// http反向代理的服务注册扩展
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// 添加https反向代理
/// 添加http反向代理
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
@ -18,8 +18,7 @@ namespace FastGithub
return services
.AddMemoryCache()
.AddHttpForwarder()
.AddSingleton<CertService>()
.AddSingleton<PortService>()
.AddSingleton<CertService>()
.AddSingleton<HttpProxyMiddleware>()
.AddSingleton<RequestLoggingMiddleware>()
.AddSingleton<HttpReverseProxyMiddleware>();

View File

@ -52,15 +52,15 @@ namespace FastGithub
webBuilder.UseKestrel(kestrel =>
{
kestrel.NoLimit();
kestrel.ListenHttpsReverseProxy();
if (OperatingSystem.IsWindows())
{
kestrel.ListenHttpsReverseProxy();
kestrel.ListenHttpReverseProxy();
kestrel.ListenSshReverseProxy();
}
else
{
kestrel.ListenHttpsReverseProxy();
kestrel.ListenHttpProxy();
}
});

View File

@ -1,10 +1,9 @@
using FastGithub.Configuration;
using FastGithub.ReverseProxy;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
namespace FastGithub
{
@ -40,7 +39,7 @@ namespace FastGithub
if (OperatingSystem.IsWindows())
{
services.AddDnsPoisoning();
services.AddDnsInterceptor();
}
}
@ -54,22 +53,16 @@ namespace FastGithub
{
app.UseRequestLogging();
app.UseHttpReverseProxy();
app.UseRouting();
app.UseEndpoints(endpoint => endpoint.MapFallback(context =>
{
context.Response.Redirect("https://github.com/dotnetcore/FastGithub");
return Task.CompletedTask;
}));
}
else
{
var portService = app.ApplicationServices.GetRequiredService<PortService>();
app.MapWhen(context => context.Connection.LocalPort == portService.HttpProxyPort, appBuilder =>
var httpProxyPort = app.ApplicationServices.GetRequiredService<IOptions<FastGithubOptions>>().Value.HttpProxyPort;
app.MapWhen(context => context.Connection.LocalPort == httpProxyPort, appBuilder =>
{
appBuilder.UseHttpProxy();
});
app.MapWhen(context => context.Connection.LocalPort != portService.HttpProxyPort, appBuilder =>
app.MapWhen(context => context.Connection.LocalPort != httpProxyPort, appBuilder =>
{
appBuilder.UseRequestLogging();
appBuilder.UseHttpReverseProxy();