合并http代理
This commit is contained in:
parent
7187e4302a
commit
9c2774dfa4
@ -8,6 +8,11 @@ namespace FastGithub.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class FastGithubOptions
|
public class FastGithubOptions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// http代理端口
|
||||||
|
/// </summary>
|
||||||
|
public int HttpProxyPort { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 回退的dns
|
/// 回退的dns
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -85,7 +85,7 @@ namespace FastGithub.Configuration
|
|||||||
/// <param name="addressFamily"></param>
|
/// <param name="addressFamily"></param>
|
||||||
/// <param name="min">最小值</param>
|
/// <param name="min">最小值</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static int GetAvailablePort(AddressFamily addressFamily, int min = 1024)
|
public static int GetAvailablePort(AddressFamily addressFamily, int min = 1025)
|
||||||
{
|
{
|
||||||
var hashSet = new HashSet<int>();
|
var hashSet = new HashSet<int>();
|
||||||
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
|
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
|
||||||
|
|||||||
51
FastGithub.Dns/DnsDnsPoisoningHostedService.cs
Normal file
51
FastGithub.Dns/DnsDnsPoisoningHostedService.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FastGithub.Dns
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// dns投毒后台服务
|
||||||
|
/// </summary>
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
sealed class DnsDnsPoisoningHostedService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly DnsPoisoningServer dnsPoisoningServer;
|
||||||
|
private readonly IEnumerable<IConflictValidator> conflictValidators;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// dns后台服务
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dnsPoisoningServer"></param>
|
||||||
|
/// <param name="conflictValidators"></param>
|
||||||
|
public DnsDnsPoisoningHostedService(
|
||||||
|
DnsPoisoningServer dnsPoisoningServer,
|
||||||
|
IEnumerable<IConflictValidator> conflictValidators)
|
||||||
|
{
|
||||||
|
this.dnsPoisoningServer = dnsPoisoningServer;
|
||||||
|
this.conflictValidators = conflictValidators;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// dns后台
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stoppingToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
foreach (var item in this.conflictValidators)
|
||||||
|
{
|
||||||
|
await item.ValidateAsync();
|
||||||
|
}
|
||||||
|
this.dnsPoisoningServer.DnsPoisoning(stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,23 +0,0 @@
|
|||||||
using FastGithub.Dns;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace FastGithub
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// DoH的中间件扩展
|
|
||||||
/// </summary>
|
|
||||||
public static class DnsOverHttpsApplicationBuilderExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 使用DoH的中间件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="app"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IApplicationBuilder UseDnsOverHttps(this IApplicationBuilder app)
|
|
||||||
{
|
|
||||||
var middleware = app.ApplicationServices.GetRequiredService<DnsOverHttpsMiddleware>();
|
|
||||||
return app.Use(next => context => middleware.InvokeAsync(context, next));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,129 +0,0 @@
|
|||||||
using DNS.Protocol;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FastGithub.Dns
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// DoH中间件
|
|
||||||
/// </summary>
|
|
||||||
sealed class DnsOverHttpsMiddleware
|
|
||||||
{
|
|
||||||
private static readonly PathString dnsQueryPath = "/dns-query";
|
|
||||||
private const string MEDIA_TYPE = "application/dns-message";
|
|
||||||
private readonly RequestResolver requestResolver;
|
|
||||||
private readonly ILogger<DnsOverHttpsMiddleware> logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DoH中间件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="requestResolver"></param>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
public DnsOverHttpsMiddleware(
|
|
||||||
RequestResolver requestResolver,
|
|
||||||
ILogger<DnsOverHttpsMiddleware> logger)
|
|
||||||
{
|
|
||||||
this.requestResolver = requestResolver;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 执行请求
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <param name="next"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
|
||||||
{
|
|
||||||
Request? request;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
request = await ParseDnsRequestAsync(context.Request);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request == null)
|
|
||||||
{
|
|
||||||
await next(context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await this.ResolveAsync(context, request);
|
|
||||||
context.Response.ContentType = MEDIA_TYPE;
|
|
||||||
await context.Response.BodyWriter.WriteAsync(response.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析dns域名
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task<IResponse> ResolveAsync(HttpContext context, Request request)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var remoteIPAddress = context.Connection.RemoteIpAddress ?? IPAddress.Loopback;
|
|
||||||
var remoteEndPoint = new IPEndPoint(remoteIPAddress, context.Connection.RemotePort);
|
|
||||||
var remoteEndPointRequest = new RemoteEndPointRequest(request, remoteEndPoint);
|
|
||||||
return await this.requestResolver.Resolve(remoteEndPointRequest);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
this.logger.LogWarning($"处理DNS异常:{ex.Message}");
|
|
||||||
return Response.FromRequest(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析dns请求
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static async Task<Request?> ParseDnsRequestAsync(HttpRequest request)
|
|
||||||
{
|
|
||||||
if (request.Path != dnsQueryPath ||
|
|
||||||
request.Headers.TryGetValue("accept", out var accept) == false ||
|
|
||||||
accept.Contains(MEDIA_TYPE) == false)
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.Method == HttpMethods.Get)
|
|
||||||
{
|
|
||||||
if (request.Query.TryGetValue("dns", out var dns) == false)
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dnsRequest = dns.ToString().Replace('-', '+').Replace('_', '/');
|
|
||||||
int mod = dnsRequest.Length % 4;
|
|
||||||
if (mod > 0)
|
|
||||||
{
|
|
||||||
dnsRequest = dnsRequest.PadRight(dnsRequest.Length - mod + 4, '=');
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = Convert.FromBase64String(dnsRequest);
|
|
||||||
return Request.FromArray(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.Method == HttpMethods.Post && request.ContentType == MEDIA_TYPE)
|
|
||||||
{
|
|
||||||
using var message = new MemoryStream();
|
|
||||||
await request.Body.CopyToAsync(message);
|
|
||||||
return Request.FromArray(message.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,107 +0,0 @@
|
|||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FastGithub.Dns
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// dns后台服务
|
|
||||||
/// </summary>
|
|
||||||
sealed class DnsOverUdpHostedService : BackgroundService
|
|
||||||
{
|
|
||||||
private readonly DnsOverUdpServer dnsOverUdpServer;
|
|
||||||
private readonly DnsPoisoningServer dnsPoisoningServer;
|
|
||||||
private readonly IEnumerable<IConflictValidator> conflictValidators;
|
|
||||||
private readonly ILogger<DnsOverUdpHostedService> logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// dns后台服务
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dnsOverUdpServer"></param>
|
|
||||||
/// <param name="dnsPoisoningServer"></param>
|
|
||||||
/// <param name="conflictValidators"></param>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
public DnsOverUdpHostedService(
|
|
||||||
DnsOverUdpServer dnsOverUdpServer,
|
|
||||||
DnsPoisoningServer dnsPoisoningServer,
|
|
||||||
IEnumerable<IConflictValidator> conflictValidators,
|
|
||||||
ILogger<DnsOverUdpHostedService> logger)
|
|
||||||
{
|
|
||||||
this.dnsOverUdpServer = dnsOverUdpServer;
|
|
||||||
this.dnsPoisoningServer = dnsPoisoningServer;
|
|
||||||
this.conflictValidators = conflictValidators;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启动dns
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows() == false)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const int DNS_PORT = 53;
|
|
||||||
this.dnsOverUdpServer.Listen(IPAddress.Any, DNS_PORT);
|
|
||||||
this.logger.LogInformation($"已监听udp端口{DNS_PORT},DNS服务启动完成");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
var builder = new StringBuilder().AppendLine($"DNS服务启动失败({ex.Message}),你可以选择如下的一种操作:");
|
|
||||||
builder.AppendLine($"1. 关闭占用udp53端口的进程然后重新打开本程序");
|
|
||||||
builder.AppendLine($"2. 配置系统或浏览器使用DNS over HTTPS:https://127.0.0.1/dns-query");
|
|
||||||
builder.AppendLine($"3. 向系统hosts文件添加github相关域名的ip为127.0.0.1");
|
|
||||||
builder.Append($"4. 在局域网其它设备上运行本程序,然后将本机DNS设置为局域网设备的IP");
|
|
||||||
this.logger.LogError(builder.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in this.conflictValidators)
|
|
||||||
{
|
|
||||||
await item.ValidateAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
await base.StartAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// dns后台
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stoppingToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
this.dnsPoisoningServer.DnsPoisoning(stoppingToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await this.dnsOverUdpServer.HandleAsync(stoppingToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 停止dns服务
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows() == false)
|
|
||||||
{
|
|
||||||
this.dnsOverUdpServer.Dispose();
|
|
||||||
}
|
|
||||||
return base.StopAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,141 +0,0 @@
|
|||||||
using DNS.Protocol;
|
|
||||||
using FastGithub.Configuration;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FastGithub.Dns
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// dns服务器
|
|
||||||
/// </summary>
|
|
||||||
sealed class DnsOverUdpServer : IDisposable
|
|
||||||
{
|
|
||||||
private readonly RequestResolver requestResolver;
|
|
||||||
private readonly ILogger<DnsOverUdpServer> logger;
|
|
||||||
private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
|
||||||
private readonly byte[] buffer = new byte[ushort.MaxValue];
|
|
||||||
|
|
||||||
private bool listened = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// dns服务器
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="requestResolver"></param>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
public DnsOverUdpServer(
|
|
||||||
RequestResolver requestResolver,
|
|
||||||
ILogger<DnsOverUdpServer> logger)
|
|
||||||
{
|
|
||||||
this.requestResolver = requestResolver;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 监听IP地址和端口
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address"></param>
|
|
||||||
/// <param name="port"></param>
|
|
||||||
/// <exception cref="SocketException"></exception>
|
|
||||||
/// <exception cref="FastGithubException"></exception>
|
|
||||||
public void Listen(IPAddress address, int port)
|
|
||||||
{
|
|
||||||
if (LocalMachine.CanListenUdp(port) == false)
|
|
||||||
{
|
|
||||||
throw new FastGithubException($"udp端口{port}已经被其它进程占用");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
const int SIO_UDP_CONNRESET = unchecked((int)0x9800000C);
|
|
||||||
this.socket.IOControl(SIO_UDP_CONNRESET, new byte[4], new byte[4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.Bind(new IPEndPoint(address, port));
|
|
||||||
this.listened = true;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SystemDnsUtil.SetAsPrimitiveDns();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
this.logger.LogWarning(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 监听和处理dns请求
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task HandleAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (this.listened == false)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
|
||||||
while (cancellationToken.IsCancellationRequested == false)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var result = await this.socket.ReceiveFromAsync(this.buffer, SocketFlags.None, remoteEndPoint);
|
|
||||||
var datas = new byte[result.ReceivedBytes];
|
|
||||||
this.buffer.AsSpan(0, datas.Length).CopyTo(datas);
|
|
||||||
this.HandleRequestAsync(datas, result.RemoteEndPoint, cancellationToken);
|
|
||||||
}
|
|
||||||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理dns请求
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="datas"></param>
|
|
||||||
/// <param name="remoteEndPoint"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
private async void HandleRequestAsync(byte[] datas, EndPoint remoteEndPoint, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var request = Request.FromArray(datas);
|
|
||||||
var remoteEndPointRequest = new RemoteEndPointRequest(request, remoteEndPoint);
|
|
||||||
var response = await this.requestResolver.Resolve(remoteEndPointRequest, cancellationToken);
|
|
||||||
await this.socket.SendToAsync(response.ToArray(), SocketFlags.None, remoteEndPoint);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
this.logger.LogWarning($"处理DNS异常:{ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 释放资源
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
this.socket.Dispose();
|
|
||||||
if (this.listened == false)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SystemDnsUtil.RemoveFromPrimitiveDns();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
this.logger.LogWarning(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -16,6 +16,7 @@ namespace FastGithub.Dns
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// dns投毒服务
|
/// dns投毒服务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
sealed class DnsPoisoningServer
|
sealed class DnsPoisoningServer
|
||||||
{
|
{
|
||||||
const string DNS_FILTER = "udp.DstPort == 53";
|
const string DNS_FILTER = "udp.DstPort == 53";
|
||||||
@ -26,7 +27,6 @@ namespace FastGithub.Dns
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 刷新DNS缓存
|
/// 刷新DNS缓存
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
[DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)]
|
[DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)]
|
||||||
private static extern void DnsFlushResolverCache();
|
private static extern void DnsFlushResolverCache();
|
||||||
|
|
||||||
@ -47,7 +47,6 @@ namespace FastGithub.Dns
|
|||||||
/// DNS投毒
|
/// DNS投毒
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
public void DnsPoisoning(CancellationToken cancellationToken)
|
public void DnsPoisoning(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var handle = WinDivert.WinDivertOpen(DNS_FILTER, WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
|
var handle = WinDivert.WinDivertOpen(DNS_FILTER, WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
using DNS.Protocol;
|
|
||||||
using FastGithub.Configuration;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace FastGithub.Dns
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 带远程终节点的请求
|
|
||||||
/// </summary>
|
|
||||||
sealed class RemoteEndPointRequest : Request
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取程终节点
|
|
||||||
/// </summary>
|
|
||||||
public EndPoint RemoteEndPoint { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程请求
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="remoteEndPoint"></param>
|
|
||||||
public RemoteEndPointRequest(Request request, EndPoint remoteEndPoint)
|
|
||||||
: base(request)
|
|
||||||
{
|
|
||||||
this.RemoteEndPoint = remoteEndPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取对应的本机地址
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public IPAddress? GetLocalIPAddress()
|
|
||||||
{
|
|
||||||
return LocalMachine.GetLocalIPAddress(this.RemoteEndPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
using DNS.Client.RequestResolver;
|
|
||||||
using DNS.Protocol;
|
|
||||||
using DNS.Protocol.ResourceRecords;
|
|
||||||
using FastGithub.Configuration;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FastGithub.Dns
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// dns解析者
|
|
||||||
/// </summary>
|
|
||||||
sealed class RequestResolver : IRequestResolver
|
|
||||||
{
|
|
||||||
private readonly TimeSpan ttl = TimeSpan.FromMinutes(1d);
|
|
||||||
private readonly FastGithubConfig fastGithubConfig;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// dns解析者
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fastGithubConfig"></param>
|
|
||||||
public RequestResolver(FastGithubConfig fastGithubConfig)
|
|
||||||
{
|
|
||||||
this.fastGithubConfig = fastGithubConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析域名
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<IResponse> Resolve(IRequest request, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var response = Response.FromRequest(request);
|
|
||||||
if (request is not RemoteEndPointRequest remoteEndPointRequest)
|
|
||||||
{
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
var question = request.Questions.FirstOrDefault();
|
|
||||||
if (question == null || question.Type != RecordType.A)
|
|
||||||
{
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析匹配的域名指向本机ip
|
|
||||||
var domain = question.Name;
|
|
||||||
if (this.fastGithubConfig.IsMatch(domain.ToString()) == true)
|
|
||||||
{
|
|
||||||
var localAddress = remoteEndPointRequest.GetLocalIPAddress() ?? IPAddress.Loopback;
|
|
||||||
var record = new IPAddressResourceRecord(domain, localAddress, this.ttl);
|
|
||||||
response.AnswerRecords.Add(record);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用回退dns解析域名
|
|
||||||
foreach (var dns in this.fastGithubConfig.FallbackDns)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var fallbackResolver = new UdpRequestResolver(dns);
|
|
||||||
var fallbackResponse = await fallbackResolver.Resolve(request, cancellationToken);
|
|
||||||
if (fallbackResponse != null && fallbackResponse.AnswerRecords.Count > 0)
|
|
||||||
{
|
|
||||||
return fallbackResponse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using FastGithub.Dns;
|
using FastGithub.Dns;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace FastGithub
|
namespace FastGithub
|
||||||
{
|
{
|
||||||
@ -10,19 +11,17 @@ namespace FastGithub
|
|||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注册dns服务
|
/// 注册dns投毒服务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services"></param>
|
/// <param name="services"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IServiceCollection AddDnsServer(this IServiceCollection services)
|
[SupportedOSPlatform("windows")]
|
||||||
|
public static IServiceCollection AddDnsPoisoning(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.TryAddSingleton<RequestResolver>();
|
|
||||||
services.TryAddSingleton<DnsOverUdpServer>();
|
|
||||||
services.TryAddSingleton<DnsPoisoningServer>();
|
services.TryAddSingleton<DnsPoisoningServer>();
|
||||||
services.TryAddSingleton<DnsOverHttpsMiddleware>();
|
|
||||||
services.AddSingleton<IConflictValidator, HostsConflictValidator>();
|
services.AddSingleton<IConflictValidator, HostsConflictValidator>();
|
||||||
services.AddSingleton<IConflictValidator, ProxyConflictValidtor>();
|
services.AddSingleton<IConflictValidator, ProxyConflictValidtor>();
|
||||||
return services.AddHostedService<DnsOverUdpHostedService>();
|
return services.AddHostedService<DnsDnsPoisoningHostedService>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,89 +0,0 @@
|
|||||||
using FastGithub.Configuration;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
|
|
||||||
namespace FastGithub.Dns
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 系统域名服务工具
|
|
||||||
/// </summary>
|
|
||||||
static class SystemDnsUtil
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 设置为主dns
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="FastGithubException"></exception>
|
|
||||||
public static void SetAsPrimitiveDns()
|
|
||||||
{
|
|
||||||
var @interface = GetOutboundNetworkInterface();
|
|
||||||
if (@interface == null)
|
|
||||||
{
|
|
||||||
throw new FastGithubException($"找不到匹配的网络适配器来设置主DNS");
|
|
||||||
}
|
|
||||||
|
|
||||||
var dnsAddresses = @interface.GetIPProperties().DnsAddresses;
|
|
||||||
var firstRecord = dnsAddresses.FirstOrDefault();
|
|
||||||
if (firstRecord == null || LocalMachine.ContainsIPAddress(firstRecord) == false)
|
|
||||||
{
|
|
||||||
var primitive = IPAddress.Loopback;
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
throw new FastGithubException($"不支持自动设置本机DNS,请手工添加{primitive}做为/etc/resolv.conf的第一条记录");
|
|
||||||
}
|
|
||||||
else if (OperatingSystem.IsMacOS())
|
|
||||||
{
|
|
||||||
throw new FastGithubException($"不支持自动设置本机DNS,请手工添加{primitive}做为连接网络的DNS的第一条记录");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从主dns移除
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="FastGithubException"></exception>
|
|
||||||
public static void RemoveFromPrimitiveDns()
|
|
||||||
{
|
|
||||||
var @interface = GetOutboundNetworkInterface();
|
|
||||||
if (@interface == null)
|
|
||||||
{
|
|
||||||
throw new FastGithubException($"找不到匹配的网络适配器来移除主DNS");
|
|
||||||
}
|
|
||||||
|
|
||||||
var dnsAddresses = @interface.GetIPProperties().DnsAddresses;
|
|
||||||
var firstRecord = dnsAddresses.FirstOrDefault();
|
|
||||||
if (firstRecord != null && LocalMachine.ContainsIPAddress(firstRecord))
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
throw new FastGithubException($"不支持自动移除本机主DNS,请手工移除/etc/resolv.conf的第一条记录");
|
|
||||||
}
|
|
||||||
else if (OperatingSystem.IsMacOS())
|
|
||||||
{
|
|
||||||
throw new FastGithubException($"不支持自动移除本机主DNS,请手工移除连接网络的DNS的第一条记录");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找出口的网络适器
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static NetworkInterface? GetOutboundNetworkInterface()
|
|
||||||
{
|
|
||||||
var remoteEndPoint = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 53);
|
|
||||||
var address = LocalMachine.GetLocalIPAddress(remoteEndPoint);
|
|
||||||
if (address == null)
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NetworkInterface
|
|
||||||
.GetAllNetworkInterfaces()
|
|
||||||
.Where(item => item.GetIPProperties().UnicastAddresses.Any(a => a.Address.Equals(address)))
|
|
||||||
.FirstOrDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,6 +9,17 @@ namespace FastGithub
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ApplicationBuilderExtensions
|
public static class ApplicationBuilderExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 使用http代理中间件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IApplicationBuilder UseHttpProxy(this IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
var middleware = app.ApplicationServices.GetRequiredService<HttpProxyMiddleware>();
|
||||||
|
return app.Use(next => context => middleware.InvokeAsync(context, next));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用请求日志中间件
|
/// 使用请求日志中间件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -25,9 +36,9 @@ namespace FastGithub
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="app"></param>
|
/// <param name="app"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IApplicationBuilder UseReverseProxy(this IApplicationBuilder app)
|
public static IApplicationBuilder UseHttpReverseProxy(this IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
var middleware = app.ApplicationServices.GetRequiredService<ReverseProxyMiddleware>();
|
var middleware = app.ApplicationServices.GetRequiredService<HttpReverseProxyMiddleware>();
|
||||||
return app.Use(next => context => middleware.InvokeAsync(context, next));
|
return app.Use(next => context => middleware.InvokeAsync(context, next));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
112
FastGithub.ReverseProxy/HttpProxyMiddleware.cs
Normal file
112
FastGithub.ReverseProxy/HttpProxyMiddleware.cs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
using FastGithub.Configuration;
|
||||||
|
using FastGithub.DomainResolve;
|
||||||
|
using Microsoft.AspNetCore.Connections.Features;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using System.IO.Pipelines;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Yarp.ReverseProxy.Forwarder;
|
||||||
|
|
||||||
|
namespace FastGithub.ReverseProxy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// http代理中间件
|
||||||
|
/// </summary>
|
||||||
|
sealed class HttpProxyMiddleware
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
/// http代理中间件
|
||||||
|
/// </summary>
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (context.Request.Method != HttpMethods.Connect)
|
||||||
|
{
|
||||||
|
var httpClient = new HttpMessageInvoker(this.socketsHttpHandler, false);
|
||||||
|
var destinationPrefix = $"{context.Request.Scheme}://{context.Request.Host}";
|
||||||
|
await this.httpForwarder.SendAsync(context, destinationPrefix, httpClient);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var endpoint = await this.GetTargetEndPointAsync(context.Request);
|
||||||
|
using var targetSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
await targetSocket.ConnectAsync(endpoint);
|
||||||
|
|
||||||
|
context.Response.StatusCode = StatusCodes.Status200OK;
|
||||||
|
context.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Connection Established";
|
||||||
|
await context.Response.CompleteAsync();
|
||||||
|
|
||||||
|
var transport = context.Features.Get<IConnectionTransportFeature>()?.Transport;
|
||||||
|
if (transport != null)
|
||||||
|
{
|
||||||
|
var targetStream = new NetworkStream(targetSocket, ownsSocket: false);
|
||||||
|
var task1 = targetStream.CopyToAsync(transport.Output);
|
||||||
|
var task2 = transport.Input.CopyToAsync(targetStream);
|
||||||
|
await Task.WhenAny(task1, task2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取目标终节点
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task<EndPoint> GetTargetEndPointAsync(HttpRequest request)
|
||||||
|
{
|
||||||
|
var domain = request.Host.Host;
|
||||||
|
var port = request.Host.Port ?? 443;
|
||||||
|
|
||||||
|
if (IPAddress.TryParse(domain, out var address) == true)
|
||||||
|
{
|
||||||
|
return new IPEndPoint(address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fastGithubConfig.TryGetDomainConfig(domain, out _) == false)
|
||||||
|
{
|
||||||
|
return new DnsEndPoint(domain, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https,走反向代理中间人
|
||||||
|
if (port == 443)
|
||||||
|
{
|
||||||
|
return new IPEndPoint(IPAddress.Loopback, this.portService.HttpsReverseProxyPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dns优选
|
||||||
|
address = await this.domainResolver.ResolveAsync(new DnsEndPoint(domain, port));
|
||||||
|
return new IPEndPoint(address, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,18 +11,18 @@ namespace FastGithub.ReverseProxy
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 反向代理中间件
|
/// 反向代理中间件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
sealed class ReverseProxyMiddleware
|
sealed class HttpReverseProxyMiddleware
|
||||||
{
|
{
|
||||||
private readonly IHttpForwarder httpForwarder;
|
private readonly IHttpForwarder httpForwarder;
|
||||||
private readonly IHttpClientFactory httpClientFactory;
|
private readonly IHttpClientFactory httpClientFactory;
|
||||||
private readonly FastGithubConfig fastGithubConfig;
|
private readonly FastGithubConfig fastGithubConfig;
|
||||||
private readonly ILogger<ReverseProxyMiddleware> logger;
|
private readonly ILogger<HttpReverseProxyMiddleware> logger;
|
||||||
|
|
||||||
public ReverseProxyMiddleware(
|
public HttpReverseProxyMiddleware(
|
||||||
IHttpForwarder httpForwarder,
|
IHttpForwarder httpForwarder,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
FastGithubConfig fastGithubConfig,
|
FastGithubConfig fastGithubConfig,
|
||||||
ILogger<ReverseProxyMiddleware> logger)
|
ILogger<HttpReverseProxyMiddleware> logger)
|
||||||
{
|
{
|
||||||
this.httpForwarder = httpForwarder;
|
this.httpForwarder = httpForwarder;
|
||||||
this.httpClientFactory = httpClientFactory;
|
this.httpClientFactory = httpClientFactory;
|
||||||
@ -25,24 +25,40 @@ namespace FastGithub
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 监听ssh
|
/// 监听http代理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kestrel"></param>
|
/// <param name="kestrel"></param>
|
||||||
public static void ListenSsh(this KestrelServerOptions kestrel)
|
public static void ListenHttpProxy(this KestrelServerOptions kestrel)
|
||||||
|
{
|
||||||
|
var httpPort = kestrel.ApplicationServices.GetRequiredService<PortService>().HttpProxyPort;
|
||||||
|
if (LocalMachine.CanListenTcp(httpPort) == false)
|
||||||
|
{
|
||||||
|
throw new FastGithubException("tcp端口{httpsPort}已经被其它进程占用,请在配置文件更换一个端口");
|
||||||
|
}
|
||||||
|
|
||||||
|
kestrel.Listen(IPAddress.Any, httpPort);
|
||||||
|
kestrel.GetLogger().LogInformation($"已监听tcp端口{httpPort},http代理启动完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 监听ssh反向代理
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kestrel"></param>
|
||||||
|
public static void ListenSshReverseProxy(this KestrelServerOptions kestrel)
|
||||||
{
|
{
|
||||||
const int SSH_PORT = 22;
|
const int SSH_PORT = 22;
|
||||||
if (LocalMachine.CanListenTcp(SSH_PORT) == true)
|
if (LocalMachine.CanListenTcp(SSH_PORT) == true)
|
||||||
{
|
{
|
||||||
kestrel.Listen(IPAddress.Any, SSH_PORT, listen => listen.UseConnectionHandler<GithubSshProxyHandler>());
|
kestrel.Listen(IPAddress.Any, SSH_PORT, listen => listen.UseConnectionHandler<SshReverseProxyHandler>());
|
||||||
kestrel.GetLogger().LogInformation($"已监听tcp端口{SSH_PORT},github的ssh代理启动完成");
|
kestrel.GetLogger().LogInformation($"已监听tcp端口{SSH_PORT},github的ssh代理启动完成");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 监听http
|
/// 监听http反向代理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kestrel"></param>
|
/// <param name="kestrel"></param>
|
||||||
public static void ListenHttp(this KestrelServerOptions kestrel)
|
public static void ListenHttpReverseProxy(this KestrelServerOptions kestrel)
|
||||||
{
|
{
|
||||||
const int HTTP_PORT = 80;
|
const int HTTP_PORT = 80;
|
||||||
if (LocalMachine.CanListenTcp(HTTP_PORT) == true)
|
if (LocalMachine.CanListenTcp(HTTP_PORT) == true)
|
||||||
@ -53,34 +69,34 @@ namespace FastGithub
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 监听https
|
/// 监听https反向代理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kestrel"></param>
|
/// <param name="kestrel"></param>
|
||||||
/// <exception cref="FastGithubException"></exception>
|
/// <exception cref="FastGithubException"></exception>
|
||||||
public static void ListenHttps(this KestrelServerOptions kestrel)
|
public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
|
||||||
{
|
{
|
||||||
const int HTTPS_PORT = 443;
|
var httpsPort = kestrel.ApplicationServices.GetRequiredService<PortService>().HttpsReverseProxyPort;
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
TcpTable.KillPortOwner(HTTPS_PORT);
|
TcpTable.KillPortOwner(httpsPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LocalMachine.CanListenTcp(HTTPS_PORT) == false)
|
if (LocalMachine.CanListenTcp(httpsPort) == false)
|
||||||
{
|
{
|
||||||
throw new FastGithubException($"tcp端口{HTTPS_PORT}已经被其它进程占用");
|
throw new FastGithubException($"tcp端口{httpsPort}已经被其它进程占用");
|
||||||
}
|
}
|
||||||
|
|
||||||
var certService = kestrel.ApplicationServices.GetRequiredService<CertService>();
|
var certService = kestrel.ApplicationServices.GetRequiredService<CertService>();
|
||||||
certService.CreateCaCertIfNotExists();
|
certService.CreateCaCertIfNotExists();
|
||||||
certService.InstallAndTrustCaCert();
|
certService.InstallAndTrustCaCert();
|
||||||
|
|
||||||
kestrel.Listen(IPAddress.Any, HTTPS_PORT,
|
kestrel.Listen(IPAddress.Any, httpsPort,
|
||||||
listen => listen.UseHttps(https =>
|
listen => listen.UseHttps(https =>
|
||||||
https.ServerCertificateSelector = (ctx, domain) =>
|
https.ServerCertificateSelector = (ctx, domain) =>
|
||||||
certService.GetOrCreateServerCert(domain)));
|
certService.GetOrCreateServerCert(domain)));
|
||||||
|
|
||||||
var logger = kestrel.GetLogger();
|
var logger = kestrel.GetLogger();
|
||||||
logger.LogInformation($"已监听tcp端口{HTTPS_PORT},https反向代理启动完成");
|
logger.LogInformation($"已监听tcp端口{httpsPort},https反向代理启动完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
48
FastGithub.ReverseProxy/PortService.cs
Normal file
48
FastGithub.ReverseProxy/PortService.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,8 +19,10 @@ namespace FastGithub
|
|||||||
.AddMemoryCache()
|
.AddMemoryCache()
|
||||||
.AddHttpForwarder()
|
.AddHttpForwarder()
|
||||||
.AddSingleton<CertService>()
|
.AddSingleton<CertService>()
|
||||||
|
.AddSingleton<PortService>()
|
||||||
|
.AddSingleton<HttpProxyMiddleware>()
|
||||||
.AddSingleton<RequestLoggingMiddleware>()
|
.AddSingleton<RequestLoggingMiddleware>()
|
||||||
.AddSingleton<ReverseProxyMiddleware>();
|
.AddSingleton<HttpReverseProxyMiddleware>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ namespace FastGithub.ReverseProxy
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// github的ssh代理处理者
|
/// github的ssh代理处理者
|
||||||
/// </summary>
|
/// </summary>
|
||||||
sealed class GithubSshProxyHandler : ConnectionHandler
|
sealed class SshReverseProxyHandler : ConnectionHandler
|
||||||
{
|
{
|
||||||
private readonly IDomainResolver domainResolver;
|
private readonly IDomainResolver domainResolver;
|
||||||
private readonly DnsEndPoint githubSshEndPoint = new("ssh.github.com", 443);
|
private readonly DnsEndPoint githubSshEndPoint = new("ssh.github.com", 443);
|
||||||
@ -19,7 +19,7 @@ namespace FastGithub.ReverseProxy
|
|||||||
/// github的ssh代理处理者
|
/// github的ssh代理处理者
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="domainResolver"></param>
|
/// <param name="domainResolver"></param>
|
||||||
public GithubSshProxyHandler(IDomainResolver domainResolver)
|
public SshReverseProxyHandler(IDomainResolver domainResolver)
|
||||||
{
|
{
|
||||||
this.domainResolver = domainResolver;
|
this.domainResolver = domainResolver;
|
||||||
}
|
}
|
||||||
@ -52,9 +52,17 @@ namespace FastGithub
|
|||||||
webBuilder.UseKestrel(kestrel =>
|
webBuilder.UseKestrel(kestrel =>
|
||||||
{
|
{
|
||||||
kestrel.NoLimit();
|
kestrel.NoLimit();
|
||||||
kestrel.ListenHttps();
|
kestrel.ListenHttpsReverseProxy();
|
||||||
kestrel.ListenHttp();
|
|
||||||
kestrel.ListenSsh();
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
kestrel.ListenHttpReverseProxy();
|
||||||
|
kestrel.ListenSshReverseProxy();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kestrel.ListenHttpProxy();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
webBuilder.UseSerilog((hosting, logger) =>
|
webBuilder.UseSerilog((hosting, logger) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
using FastGithub.Configuration;
|
using FastGithub.Configuration;
|
||||||
|
using FastGithub.ReverseProxy;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.IO;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace FastGithub
|
namespace FastGithub
|
||||||
@ -34,10 +34,14 @@ namespace FastGithub
|
|||||||
|
|
||||||
services.AddConfiguration();
|
services.AddConfiguration();
|
||||||
services.AddDomainResolve();
|
services.AddDomainResolve();
|
||||||
services.AddDnsServer();
|
|
||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
services.AddReverseProxy();
|
services.AddReverseProxy();
|
||||||
services.AddHostedService<VersonHostedService>();
|
services.AddHostedService<VersonHostedService>();
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
services.AddDnsPoisoning();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -45,27 +49,32 @@ namespace FastGithub
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="app"></param>
|
/// <param name="app"></param>
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
app.UseRequestLogging();
|
app.UseRequestLogging();
|
||||||
app.UseDnsOverHttps();
|
app.UseHttpReverseProxy();
|
||||||
app.UseReverseProxy();
|
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
app.UseEndpoints(endpoint =>
|
app.UseEndpoints(endpoint => endpoint.MapFallback(context =>
|
||||||
{
|
|
||||||
endpoint.Map("/", async context =>
|
|
||||||
{
|
|
||||||
var certFile = $"CACert/{nameof(FastGithub)}.cer";
|
|
||||||
context.Response.ContentType = "application/x-x509-ca-cert";
|
|
||||||
context.Response.Headers.Add("Content-Disposition", $"attachment;filename={Path.GetFileName(certFile)}");
|
|
||||||
await context.Response.SendFileAsync(certFile);
|
|
||||||
});
|
|
||||||
endpoint.MapFallback(context =>
|
|
||||||
{
|
{
|
||||||
context.Response.Redirect("https://github.com/dotnetcore/FastGithub");
|
context.Response.Redirect("https://github.com/dotnetcore/FastGithub");
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var portService = app.ApplicationServices.GetRequiredService<PortService>();
|
||||||
|
app.MapWhen(context => context.Connection.LocalPort == portService.HttpProxyPort, appBuilder =>
|
||||||
|
{
|
||||||
|
appBuilder.UseHttpProxy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.MapWhen(context => context.Connection.LocalPort != portService.HttpProxyPort, appBuilder =>
|
||||||
|
{
|
||||||
|
appBuilder.UseRequestLogging();
|
||||||
|
appBuilder.UseHttpReverseProxy();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
// 新增的子配置文件appsettings.*.json,重启应用程序才生效
|
// 新增的子配置文件appsettings.*.json,重启应用程序才生效
|
||||||
"FastGithub": {
|
"FastGithub": {
|
||||||
|
"HttpProxyPort": 2222, // http代理端口,非windows才使用
|
||||||
"FallbackDns": [ // 用于解析不在DomainConfigs的域名
|
"FallbackDns": [ // 用于解析不在DomainConfigs的域名
|
||||||
{
|
{
|
||||||
"IPAddress": "114.114.114.114",
|
"IPAddress": "114.114.114.114",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user