增加http反向代理;
使用IPGlobalProperties获取全局ip;
This commit is contained in:
parent
ddb6747a7a
commit
48e994182f
@ -1,4 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
@ -52,17 +53,10 @@ namespace FastGithub.Configuration
|
||||
/// <returns></returns>
|
||||
private static bool IsLocalMachineIPAddress(IPAddress address)
|
||||
{
|
||||
foreach (var @interface in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
foreach (var addressInfo in @interface.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
if (addressInfo.Address.Equals(address))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return IPGlobalProperties
|
||||
.GetIPGlobalProperties()
|
||||
.GetUnicastAddresses()
|
||||
.Any(item => item.Address.Equals(address));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
using DNS.Protocol;
|
||||
using FastGithub.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -40,9 +42,15 @@ namespace FastGithub.Dns
|
||||
/// <returns></returns>
|
||||
public void Bind(IPAddress address, int port)
|
||||
{
|
||||
if (OperatingSystem.IsWindows() && UdpTable.TryGetOwnerProcessId(port, out var processId))
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
Process.GetProcessById(processId).Kill();
|
||||
UdpTable.KillPortOwner(port);
|
||||
}
|
||||
|
||||
var udpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners();
|
||||
if (udpListeners.Any(item => item.Port == port))
|
||||
{
|
||||
throw new FastGithubException($"udp端口{port}已经被其它进程占用");
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
|
||||
@ -84,15 +84,9 @@ namespace FastGithub.Dns
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<IPAddress> GetLocalMachineIPAddress()
|
||||
{
|
||||
yield return IPAddress.Loopback;
|
||||
yield return IPAddress.IPv6Loopback;
|
||||
|
||||
foreach (var @interface in NetworkInterface.GetAllNetworkInterfaces())
|
||||
foreach (var item in IPGlobalProperties.GetIPGlobalProperties().GetUnicastAddresses())
|
||||
{
|
||||
foreach (var addressInfo in @interface.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
yield return addressInfo.Address;
|
||||
}
|
||||
yield return item.Address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -18,6 +19,34 @@ namespace FastGithub.Dns
|
||||
[DllImport("iphlpapi.dll", SetLastError = true)]
|
||||
private static extern uint GetExtendedUdpTable(void* pUdpTable, ref int pdwSize, bool bOrder, AddressFamily ulAf, UDP_TABLE_CLASS tableClass, uint reserved = 0);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 杀死占用进程
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public static bool KillPortOwner(int port)
|
||||
{
|
||||
if (TryGetOwnerProcessId(port, out var pid) == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Process.GetProcessById(pid).Kill();
|
||||
return true;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取udp端口的占用进程id
|
||||
/// </summary>
|
||||
|
||||
@ -22,9 +22,34 @@ namespace FastGithub
|
||||
public static class KestrelServerOptionsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 域名证书缓存
|
||||
/// 服务器证书缓存
|
||||
/// </summary>
|
||||
private static readonly IMemoryCache domainCertCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
|
||||
private static readonly IMemoryCache serverCertCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
|
||||
|
||||
/// <summary>
|
||||
/// 监听http的反向代理
|
||||
/// </summary>
|
||||
/// <param name="kestrel"></param>
|
||||
public static void ListenHttpReverseProxy(this KestrelServerOptions kestrel)
|
||||
{
|
||||
var loggerFactory = kestrel.ApplicationServices.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(ReverseProxy)}");
|
||||
|
||||
const int HTTP_PORT = 80;
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
TcpTable.KillPortOwner(HTTP_PORT);
|
||||
}
|
||||
|
||||
if (CanTcpListen(HTTP_PORT) == false)
|
||||
{
|
||||
logger.LogWarning($"无法监听tcp端口{HTTP_PORT},{nameof(FastGithub)}无法http反向代理");
|
||||
}
|
||||
else
|
||||
{
|
||||
kestrel.Listen(IPAddress.Any, HTTP_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 监听https的反向代理
|
||||
@ -43,10 +68,34 @@ namespace FastGithub
|
||||
GeneratorCaCert(caPublicCerPath, caPrivateKeyPath);
|
||||
InstallCaCert(caPublicCerPath, logger);
|
||||
|
||||
kestrel.Listen(IPAddress.Any, 443, listen =>
|
||||
listen.UseHttps(https =>
|
||||
https.ServerCertificateSelector = (ctx, domain) =>
|
||||
GetDomainCert(domain, caPublicCerPath, caPrivateKeyPath)));
|
||||
const int HTTPS_PORT = 443;
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
TcpTable.KillPortOwner(HTTPS_PORT);
|
||||
}
|
||||
|
||||
if (CanTcpListen(HTTPS_PORT) == false)
|
||||
{
|
||||
logger.LogWarning($"无法监听tcp端口{HTTPS_PORT},{nameof(FastGithub)}无法https反向代理");
|
||||
}
|
||||
else
|
||||
{
|
||||
kestrel.Listen(IPAddress.Any, HTTPS_PORT, listen =>
|
||||
listen.UseHttps(https =>
|
||||
https.ServerCertificateSelector = (ctx, domain) =>
|
||||
GetServerCert(domain, caPublicCerPath, caPrivateKeyPath)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以监听指定端口
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
private static bool CanTcpListen(int port)
|
||||
{
|
||||
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
|
||||
return tcpListeners.Any(item => item.Port == port) == false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -81,25 +130,24 @@ namespace FastGithub
|
||||
if (OperatingSystem.IsWindows() == false)
|
||||
{
|
||||
logger.LogWarning($"不支持自动安装证书{caPublicCerPath}:请手动安装证书到根证书颁发机构");
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
var caCert = new X509Certificate2(caPublicCerPath);
|
||||
using var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
if (store.Certificates.Find(X509FindType.FindByThumbprint, caCert.Thumbprint, true).Count == 0)
|
||||
{
|
||||
var caCert = new X509Certificate2(caPublicCerPath);
|
||||
using var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
if (store.Certificates.Find(X509FindType.FindByThumbprint, caCert.Thumbprint, true).Count == 0)
|
||||
{
|
||||
store.Add(caCert);
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
logger.LogWarning($"安装证书{caPublicCerPath}失败:请手动安装到“将所有的证书都放入下载存储”\\“受信任的根证书颁发机构”");
|
||||
store.Add(caCert);
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
logger.LogWarning($"安装证书{caPublicCerPath}失败:请手动安装到“将所有的证书都放入下载存储”\\“受信任的根证书颁发机构”");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -109,9 +157,9 @@ namespace FastGithub
|
||||
/// <param name="caPublicCerPath"></param>
|
||||
/// <param name="caPrivateKeyPath"></param>
|
||||
/// <returns></returns>
|
||||
private static X509Certificate2 GetDomainCert(string? domain, string caPublicCerPath, string caPrivateKeyPath)
|
||||
private static X509Certificate2 GetServerCert(string? domain, string caPublicCerPath, string caPrivateKeyPath)
|
||||
{
|
||||
return domainCertCache.GetOrCreate(domain ?? string.Empty, GetOrCreateCert);
|
||||
return serverCertCache.GetOrCreate(domain ?? string.Empty, GetOrCreateCert);
|
||||
|
||||
// 生成域名的1年证书
|
||||
X509Certificate2 GetOrCreateCert(ICacheEntry entry)
|
||||
@ -138,17 +186,14 @@ namespace FastGithub
|
||||
yield return host;
|
||||
}
|
||||
|
||||
yield return Environment.MachineName;
|
||||
yield return IPAddress.Loopback.ToString();
|
||||
var globalPropreties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
|
||||
foreach (var @interface in NetworkInterface.GetAllNetworkInterfaces())
|
||||
yield return globalPropreties.HostName;
|
||||
foreach (var item in globalPropreties.GetUnicastAddresses())
|
||||
{
|
||||
foreach (var addressInfo in @interface.GetIPProperties().UnicastAddresses)
|
||||
if (item.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
if (addressInfo.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
yield return addressInfo.Address.ToString();
|
||||
}
|
||||
yield return item.Address.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,11 +21,11 @@ namespace FastGithub
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用https反向代理中间件
|
||||
/// 使用反向代理中间件
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseHttpsReverseProxy(this IApplicationBuilder app)
|
||||
public static IApplicationBuilder UseReverseProxy(this IApplicationBuilder app)
|
||||
{
|
||||
var middleware = app.ApplicationServices.GetRequiredService<ReverseProxyMiddleware>();
|
||||
return app.Use(next => context => middleware.InvokeAsync(context, next));
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastGithub.ReverseProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// 反向代理端口检测后台服务
|
||||
/// </summary>
|
||||
sealed class ReverseProxyHostedService : IHostedService
|
||||
{
|
||||
private readonly ILogger<ReverseProxyHostedService> logger;
|
||||
|
||||
/// <summary>
|
||||
/// 反向代理端口检测后台服务
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
public ReverseProxyHostedService(ILogger<ReverseProxyHostedService> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务启动时
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
const int HTTPSPORT = 443;
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
if (TcpTable.TryGetOwnerProcessId(HTTPSPORT, out var pid))
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.GetProcessById(pid).Kill();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
var processName = Process.GetProcessById(pid).ProcessName;
|
||||
this.logger.LogError($"由于进程{processName}({pid})占用了{HTTPSPORT}端口,{nameof(FastGithub)}的反向代理无法工作");
|
||||
}
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务停止时
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,7 +23,7 @@ namespace FastGithub.ReverseProxy
|
||||
IHttpClientFactory httpClientFactory,
|
||||
FastGithubConfig fastGithubConfig,
|
||||
ILogger<ReverseProxyMiddleware> logger)
|
||||
{
|
||||
{
|
||||
this.httpForwarder = httpForwarder;
|
||||
this.httpClientFactory = httpClientFactory;
|
||||
this.fastGithubConfig = fastGithubConfig;
|
||||
@ -54,7 +54,8 @@ namespace FastGithub.ReverseProxy
|
||||
}
|
||||
else
|
||||
{
|
||||
var destinationPrefix = GetDestinationPrefix(host, domainConfig.Destination);
|
||||
var scheme = context.Request.Scheme;
|
||||
var destinationPrefix = GetDestinationPrefix(scheme, host, domainConfig.Destination);
|
||||
var httpClient = this.httpClientFactory.CreateHttpClient(domainConfig);
|
||||
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient);
|
||||
await HandleErrorAsync(context, error);
|
||||
@ -64,12 +65,13 @@ namespace FastGithub.ReverseProxy
|
||||
/// <summary>
|
||||
/// 获取目标前缀
|
||||
/// </summary>
|
||||
/// <param name="host"></param>
|
||||
/// <param name="scheme"></param>
|
||||
/// <param name="host"></param>
|
||||
/// <param name="destination"></param>
|
||||
/// <returns></returns>
|
||||
private string GetDestinationPrefix(string host, Uri? destination)
|
||||
private string GetDestinationPrefix(string scheme, string host, Uri? destination)
|
||||
{
|
||||
var defaultValue = $"https://{host}/";
|
||||
var defaultValue = $"{scheme}://{host}/";
|
||||
if (destination == null)
|
||||
{
|
||||
return defaultValue;
|
||||
|
||||
@ -19,8 +19,7 @@ namespace FastGithub
|
||||
.AddMemoryCache()
|
||||
.AddHttpForwarder()
|
||||
.AddSingleton<RequestLoggingMilldeware>()
|
||||
.AddSingleton<ReverseProxyMiddleware>()
|
||||
.AddHostedService<ReverseProxyHostedService>();
|
||||
.AddSingleton<ReverseProxyMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -18,6 +19,34 @@ namespace FastGithub.ReverseProxy
|
||||
[DllImport("iphlpapi.dll", SetLastError = true)]
|
||||
private static extern uint GetExtendedTcpTable(void* pTcpTable, ref int pdwSize, bool bOrder, AddressFamily ulAf, TCP_TABLE_CLASS tableClass, uint reserved = 0);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 杀死占用进程
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public static bool KillPortOwner(int port)
|
||||
{
|
||||
if (TryGetOwnerProcessId(port, out var pid) == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Process.GetProcessById(pid).Kill();
|
||||
return true;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取tcp端口的占用进程id
|
||||
/// </summary>
|
||||
|
||||
@ -29,7 +29,7 @@ namespace FastGithub
|
||||
/// <returns></returns>
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this.logger.LogInformation($"{nameof(FastGithub)}启动完成,访问https://127.0.0.1或本机其它任意ip可进入Dashboard");
|
||||
this.logger.LogInformation($"{nameof(FastGithub)}启动完成,访问http://127.0.0.1或本机其它任意ip可进入Dashboard");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +41,11 @@ namespace FastGithub
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
webBuilder.UseKestrel(kestrel => kestrel.ListenHttpsReverseProxy());
|
||||
webBuilder.UseKestrel(kestrel =>
|
||||
{
|
||||
kestrel.ListenHttpReverseProxy();
|
||||
kestrel.ListenHttpsReverseProxy();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ namespace FastGithub
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseRequestLogging();
|
||||
app.UseHttpsReverseProxy();
|
||||
app.UseReverseProxy();
|
||||
app.UseRouting();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
|
||||
@ -11,7 +11,7 @@ github加速神器
|
||||
如果不能下载[releases](https://github.com/xljiulang/FastGithub/releases)里发布的程序,可以到Q群`307306673`里面的群文件下载。
|
||||
|
||||
### 使用说明
|
||||
运行FastGithub,然后浏览器访问 https://127.0.0.1 或其它ip进入Dashboard
|
||||
运行FastGithub,然后浏览器访问 http://127.0.0.1 或其它ip进入Dashboard
|
||||
|
||||
### 安全性说明
|
||||
FastGithub生成自签名CA证书,要求安装到本机设备。FastGithub为每台不同设备生成的CA不同的证书,保存在CACert文件夹下,请不要将证书私钥泄露给他人,以免造成损失。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user