From 9c2774dfa4d798352a5db42a81a8ae3fbcae8678 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=9B=BD=E4=BC=9F?= <366193849@qq.com>
Date: Tue, 14 Sep 2021 17:55:23 +0800
Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6http=E4=BB=A3=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
FastGithub.Configuration/FastGithubOptions.cs | 5 +
FastGithub.Configuration/LocalMachine.cs | 2 +-
.../DnsDnsPoisoningHostedService.cs | 51 +++++++
...nsOverHttpsApplicationBuilderExtensions.cs | 23 ---
FastGithub.Dns/DnsOverHttpsMiddleware.cs | 129 ----------------
FastGithub.Dns/DnsOverUdpHostedService.cs | 107 -------------
FastGithub.Dns/DnsOverUdpServer.cs | 141 ------------------
FastGithub.Dns/DnsPoisoningServer.cs | 9 +-
FastGithub.Dns/RemoteEndPointRequest.cs | 37 -----
FastGithub.Dns/RequestResolver.cs | 80 ----------
FastGithub.Dns/ServiceCollectionExtensions.cs | 13 +-
FastGithub.Dns/SystemDnsUtil.cs | 89 -----------
.../ApplicationBuilderExtensions.cs | 15 +-
.../HttpProxyMiddleware.cs | 112 ++++++++++++++
...eware.cs => HttpReverseProxyMiddleware.cs} | 8 +-
.../KestrelServerOptionsExtensions.cs | 42 ++++--
FastGithub.ReverseProxy/PortService.cs | 48 ++++++
.../ServiceCollectionExtensions.cs | 6 +-
...xyHandler.cs => SshReverseProxyHandler.cs} | 4 +-
FastGithub/Program.cs | 14 +-
FastGithub/Startup.cs | 45 +++---
FastGithub/appsettings.json | 1 +
22 files changed, 318 insertions(+), 663 deletions(-)
create mode 100644 FastGithub.Dns/DnsDnsPoisoningHostedService.cs
delete mode 100644 FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs
delete mode 100644 FastGithub.Dns/DnsOverHttpsMiddleware.cs
delete mode 100644 FastGithub.Dns/DnsOverUdpHostedService.cs
delete mode 100644 FastGithub.Dns/DnsOverUdpServer.cs
delete mode 100644 FastGithub.Dns/RemoteEndPointRequest.cs
delete mode 100644 FastGithub.Dns/RequestResolver.cs
delete mode 100644 FastGithub.Dns/SystemDnsUtil.cs
create mode 100644 FastGithub.ReverseProxy/HttpProxyMiddleware.cs
rename FastGithub.ReverseProxy/{ReverseProxyMiddleware.cs => HttpReverseProxyMiddleware.cs} (94%)
create mode 100644 FastGithub.ReverseProxy/PortService.cs
rename FastGithub.ReverseProxy/{GithubSshProxyHandler.cs => SshReverseProxyHandler.cs} (91%)
diff --git a/FastGithub.Configuration/FastGithubOptions.cs b/FastGithub.Configuration/FastGithubOptions.cs
index 69faac7..036f0ed 100644
--- a/FastGithub.Configuration/FastGithubOptions.cs
+++ b/FastGithub.Configuration/FastGithubOptions.cs
@@ -8,6 +8,11 @@ namespace FastGithub.Configuration
///
public class FastGithubOptions
{
+ ///
+ /// http代理端口
+ ///
+ public int HttpProxyPort { get; set; }
+
///
/// 回退的dns
///
diff --git a/FastGithub.Configuration/LocalMachine.cs b/FastGithub.Configuration/LocalMachine.cs
index eb63abf..bb8c842 100644
--- a/FastGithub.Configuration/LocalMachine.cs
+++ b/FastGithub.Configuration/LocalMachine.cs
@@ -85,7 +85,7 @@ namespace FastGithub.Configuration
///
/// 最小值
///
- public static int GetAvailablePort(AddressFamily addressFamily, int min = 1024)
+ public static int GetAvailablePort(AddressFamily addressFamily, int min = 1025)
{
var hashSet = new HashSet();
var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
diff --git a/FastGithub.Dns/DnsDnsPoisoningHostedService.cs b/FastGithub.Dns/DnsDnsPoisoningHostedService.cs
new file mode 100644
index 0000000..cbb6bda
--- /dev/null
+++ b/FastGithub.Dns/DnsDnsPoisoningHostedService.cs
@@ -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
+{
+ ///
+ /// dns投毒后台服务
+ ///
+ [SupportedOSPlatform("windows")]
+ sealed class DnsDnsPoisoningHostedService : BackgroundService
+ {
+ private readonly DnsPoisoningServer dnsPoisoningServer;
+ private readonly IEnumerable conflictValidators;
+
+ ///
+ /// dns后台服务
+ ///
+ ///
+ ///
+ public DnsDnsPoisoningHostedService(
+ DnsPoisoningServer dnsPoisoningServer,
+ IEnumerable conflictValidators)
+ {
+ this.dnsPoisoningServer = dnsPoisoningServer;
+ this.conflictValidators = conflictValidators;
+ }
+
+ ///
+ /// dns后台
+ ///
+ ///
+ ///
+ 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);
+ }
+ }
+ }
+}
diff --git a/FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs b/FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs
deleted file mode 100644
index 9255fd8..0000000
--- a/FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using FastGithub.Dns;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace FastGithub
-{
- ///
- /// DoH的中间件扩展
- ///
- public static class DnsOverHttpsApplicationBuilderExtensions
- {
- ///
- /// 使用DoH的中间件
- ///
- ///
- ///
- public static IApplicationBuilder UseDnsOverHttps(this IApplicationBuilder app)
- {
- var middleware = app.ApplicationServices.GetRequiredService();
- return app.Use(next => context => middleware.InvokeAsync(context, next));
- }
- }
-}
diff --git a/FastGithub.Dns/DnsOverHttpsMiddleware.cs b/FastGithub.Dns/DnsOverHttpsMiddleware.cs
deleted file mode 100644
index de63218..0000000
--- a/FastGithub.Dns/DnsOverHttpsMiddleware.cs
+++ /dev/null
@@ -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
-{
- ///
- /// DoH中间件
- ///
- 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 logger;
-
- ///
- /// DoH中间件
- ///
- ///
- ///
- public DnsOverHttpsMiddleware(
- RequestResolver requestResolver,
- ILogger logger)
- {
- this.requestResolver = requestResolver;
- this.logger = logger;
- }
-
- ///
- /// 执行请求
- ///
- ///
- ///
- ///
- 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());
- }
-
- ///
- /// 解析dns域名
- ///
- ///
- ///
- ///
- private async Task 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);
- }
- }
-
- ///
- /// 解析dns请求
- ///
- ///
- ///
- private static async Task 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;
- }
- }
-}
diff --git a/FastGithub.Dns/DnsOverUdpHostedService.cs b/FastGithub.Dns/DnsOverUdpHostedService.cs
deleted file mode 100644
index 4328e3a..0000000
--- a/FastGithub.Dns/DnsOverUdpHostedService.cs
+++ /dev/null
@@ -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
-{
- ///
- /// dns后台服务
- ///
- sealed class DnsOverUdpHostedService : BackgroundService
- {
- private readonly DnsOverUdpServer dnsOverUdpServer;
- private readonly DnsPoisoningServer dnsPoisoningServer;
- private readonly IEnumerable conflictValidators;
- private readonly ILogger logger;
-
- ///
- /// dns后台服务
- ///
- ///
- ///
- ///
- ///
- public DnsOverUdpHostedService(
- DnsOverUdpServer dnsOverUdpServer,
- DnsPoisoningServer dnsPoisoningServer,
- IEnumerable conflictValidators,
- ILogger logger)
- {
- this.dnsOverUdpServer = dnsOverUdpServer;
- this.dnsPoisoningServer = dnsPoisoningServer;
- this.conflictValidators = conflictValidators;
- this.logger = logger;
- }
-
- ///
- /// 启动dns
- ///
- ///
- ///
- 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);
- }
-
- ///
- /// dns后台
- ///
- ///
- ///
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- if (OperatingSystem.IsWindows())
- {
- await Task.Yield();
- this.dnsPoisoningServer.DnsPoisoning(stoppingToken);
- }
- else
- {
- await this.dnsOverUdpServer.HandleAsync(stoppingToken);
- }
- }
-
- ///
- /// 停止dns服务
- ///
- ///
- ///
- public override Task StopAsync(CancellationToken cancellationToken)
- {
- if (OperatingSystem.IsWindows() == false)
- {
- this.dnsOverUdpServer.Dispose();
- }
- return base.StopAsync(cancellationToken);
- }
- }
-}
diff --git a/FastGithub.Dns/DnsOverUdpServer.cs b/FastGithub.Dns/DnsOverUdpServer.cs
deleted file mode 100644
index 517d1d4..0000000
--- a/FastGithub.Dns/DnsOverUdpServer.cs
+++ /dev/null
@@ -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
-{
- ///
- /// dns服务器
- ///
- sealed class DnsOverUdpServer : IDisposable
- {
- private readonly RequestResolver requestResolver;
- private readonly ILogger logger;
- private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
- private readonly byte[] buffer = new byte[ushort.MaxValue];
-
- private bool listened = false;
-
- ///
- /// dns服务器
- ///
- ///
- ///
- public DnsOverUdpServer(
- RequestResolver requestResolver,
- ILogger logger)
- {
- this.requestResolver = requestResolver;
- this.logger = logger;
- }
-
- ///
- /// 监听IP地址和端口
- ///
- ///
- ///
- ///
- ///
- 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);
- }
- }
-
- ///
- /// 监听和处理dns请求
- ///
- ///
- ///
- 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;
- }
- }
- }
-
- ///
- /// 处理dns请求
- ///
- ///
- ///
- ///
- 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}");
- }
- }
-
- ///
- /// 释放资源
- ///
- public void Dispose()
- {
- this.socket.Dispose();
- if (this.listened == false)
- {
- return;
- }
-
- try
- {
- SystemDnsUtil.RemoveFromPrimitiveDns();
- }
- catch (Exception ex)
- {
- this.logger.LogWarning(ex.Message);
- }
- }
- }
-}
diff --git a/FastGithub.Dns/DnsPoisoningServer.cs b/FastGithub.Dns/DnsPoisoningServer.cs
index c1403ce..ee1414f 100644
--- a/FastGithub.Dns/DnsPoisoningServer.cs
+++ b/FastGithub.Dns/DnsPoisoningServer.cs
@@ -16,19 +16,19 @@ namespace FastGithub.Dns
///
/// dns投毒服务
///
+ [SupportedOSPlatform("windows")]
sealed class DnsPoisoningServer
{
const string DNS_FILTER = "udp.DstPort == 53";
private readonly FastGithubConfig fastGithubConfig;
private readonly ILogger logger;
- private readonly TimeSpan ttl = TimeSpan.FromSeconds(10d);
+ private readonly TimeSpan ttl = TimeSpan.FromSeconds(10d);
///
/// 刷新DNS缓存
- ///
- [SupportedOSPlatform("windows")]
+ ///
[DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)]
- private static extern void DnsFlushResolverCache();
+ private static extern void DnsFlushResolverCache();
///
/// dns投毒后台服务
@@ -47,7 +47,6 @@ namespace FastGithub.Dns
/// DNS投毒
///
///
- [SupportedOSPlatform("windows")]
public void DnsPoisoning(CancellationToken cancellationToken)
{
var handle = WinDivert.WinDivertOpen(DNS_FILTER, WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
diff --git a/FastGithub.Dns/RemoteEndPointRequest.cs b/FastGithub.Dns/RemoteEndPointRequest.cs
deleted file mode 100644
index a997d7e..0000000
--- a/FastGithub.Dns/RemoteEndPointRequest.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using DNS.Protocol;
-using FastGithub.Configuration;
-using System.Net;
-
-namespace FastGithub.Dns
-{
- ///
- /// 带远程终节点的请求
- ///
- sealed class RemoteEndPointRequest : Request
- {
- ///
- /// 获取程终节点
- ///
- public EndPoint RemoteEndPoint { get; }
-
- ///
- /// 远程请求
- ///
- ///
- ///
- public RemoteEndPointRequest(Request request, EndPoint remoteEndPoint)
- : base(request)
- {
- this.RemoteEndPoint = remoteEndPoint;
- }
-
- ///
- /// 获取对应的本机地址
- ///
- ///
- public IPAddress? GetLocalIPAddress()
- {
- return LocalMachine.GetLocalIPAddress(this.RemoteEndPoint);
- }
- }
-}
diff --git a/FastGithub.Dns/RequestResolver.cs b/FastGithub.Dns/RequestResolver.cs
deleted file mode 100644
index e876530..0000000
--- a/FastGithub.Dns/RequestResolver.cs
+++ /dev/null
@@ -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
-{
- ///
- /// dns解析者
- ///
- sealed class RequestResolver : IRequestResolver
- {
- private readonly TimeSpan ttl = TimeSpan.FromMinutes(1d);
- private readonly FastGithubConfig fastGithubConfig;
-
- ///
- /// dns解析者
- ///
- ///
- public RequestResolver(FastGithubConfig fastGithubConfig)
- {
- this.fastGithubConfig = fastGithubConfig;
- }
-
- ///
- /// 解析域名
- ///
- ///
- ///
- ///
- public async Task 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;
- }
- }
-}
diff --git a/FastGithub.Dns/ServiceCollectionExtensions.cs b/FastGithub.Dns/ServiceCollectionExtensions.cs
index 21c27d2..8725e49 100644
--- a/FastGithub.Dns/ServiceCollectionExtensions.cs
+++ b/FastGithub.Dns/ServiceCollectionExtensions.cs
@@ -1,6 +1,7 @@
using FastGithub.Dns;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using System.Runtime.Versioning;
namespace FastGithub
{
@@ -10,19 +11,17 @@ namespace FastGithub
public static class ServiceCollectionExtensions
{
///
- /// 注册dns服务
+ /// 注册dns投毒服务
///
///
///
- public static IServiceCollection AddDnsServer(this IServiceCollection services)
- {
- services.TryAddSingleton();
- services.TryAddSingleton();
+ [SupportedOSPlatform("windows")]
+ public static IServiceCollection AddDnsPoisoning(this IServiceCollection services)
+ {
services.TryAddSingleton();
- services.TryAddSingleton();
services.AddSingleton();
services.AddSingleton();
- return services.AddHostedService();
+ return services.AddHostedService();
}
}
}
diff --git a/FastGithub.Dns/SystemDnsUtil.cs b/FastGithub.Dns/SystemDnsUtil.cs
deleted file mode 100644
index 69a3c14..0000000
--- a/FastGithub.Dns/SystemDnsUtil.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using FastGithub.Configuration;
-using System;
-using System.Linq;
-using System.Net;
-using System.Net.NetworkInformation;
-
-namespace FastGithub.Dns
-{
- ///
- /// 系统域名服务工具
- ///
- static class SystemDnsUtil
- {
- ///
- /// 设置为主dns
- ///
- ///
- 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的第一条记录");
- }
- }
- }
-
- ///
- /// 从主dns移除
- ///
- ///
- 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的第一条记录");
- }
- }
- }
-
-
- ///
- /// 查找出口的网络适器
- ///
- ///
- 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();
- }
- }
-}
\ No newline at end of file
diff --git a/FastGithub.ReverseProxy/ApplicationBuilderExtensions.cs b/FastGithub.ReverseProxy/ApplicationBuilderExtensions.cs
index 854b684..e57b668 100644
--- a/FastGithub.ReverseProxy/ApplicationBuilderExtensions.cs
+++ b/FastGithub.ReverseProxy/ApplicationBuilderExtensions.cs
@@ -9,6 +9,17 @@ namespace FastGithub
///
public static class ApplicationBuilderExtensions
{
+ ///
+ /// 使用http代理中间件
+ ///
+ ///
+ ///
+ public static IApplicationBuilder UseHttpProxy(this IApplicationBuilder app)
+ {
+ var middleware = app.ApplicationServices.GetRequiredService();
+ return app.Use(next => context => middleware.InvokeAsync(context, next));
+ }
+
///
/// 使用请求日志中间件
///
@@ -25,9 +36,9 @@ namespace FastGithub
///
///
///
- public static IApplicationBuilder UseReverseProxy(this IApplicationBuilder app)
+ public static IApplicationBuilder UseHttpReverseProxy(this IApplicationBuilder app)
{
- var middleware = app.ApplicationServices.GetRequiredService();
+ var middleware = app.ApplicationServices.GetRequiredService();
return app.Use(next => context => middleware.InvokeAsync(context, next));
}
}
diff --git a/FastGithub.ReverseProxy/HttpProxyMiddleware.cs b/FastGithub.ReverseProxy/HttpProxyMiddleware.cs
new file mode 100644
index 0000000..9915b54
--- /dev/null
+++ b/FastGithub.ReverseProxy/HttpProxyMiddleware.cs
@@ -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
+{
+ ///
+ /// http代理中间件
+ ///
+ 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 };
+
+ ///
+ /// http代理中间件
+ ///
+ ///
+ ///
+ ///
+ ///
+ public HttpProxyMiddleware(
+ FastGithubConfig fastGithubConfig,
+ IDomainResolver domainResolver,
+ IHttpForwarder httpForwarder,
+ PortService portService)
+ {
+ this.fastGithubConfig = fastGithubConfig;
+ this.domainResolver = domainResolver;
+ this.httpForwarder = httpForwarder;
+ this.portService = portService;
+ }
+
+ ///
+ /// 处理请求
+ ///
+ ///
+ ///
+ ///
+ 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().ReasonPhrase = "Connection Established";
+ await context.Response.CompleteAsync();
+
+ var transport = context.Features.Get()?.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);
+ }
+ }
+ }
+
+
+ ///
+ /// 获取目标终节点
+ ///
+ ///
+ ///
+ private async Task 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs b/FastGithub.ReverseProxy/HttpReverseProxyMiddleware.cs
similarity index 94%
rename from FastGithub.ReverseProxy/ReverseProxyMiddleware.cs
rename to FastGithub.ReverseProxy/HttpReverseProxyMiddleware.cs
index f8b9766..5041045 100644
--- a/FastGithub.ReverseProxy/ReverseProxyMiddleware.cs
+++ b/FastGithub.ReverseProxy/HttpReverseProxyMiddleware.cs
@@ -11,18 +11,18 @@ namespace FastGithub.ReverseProxy
///
/// 反向代理中间件
///
- sealed class ReverseProxyMiddleware
+ sealed class HttpReverseProxyMiddleware
{
private readonly IHttpForwarder httpForwarder;
private readonly IHttpClientFactory httpClientFactory;
private readonly FastGithubConfig fastGithubConfig;
- private readonly ILogger logger;
+ private readonly ILogger logger;
- public ReverseProxyMiddleware(
+ public HttpReverseProxyMiddleware(
IHttpForwarder httpForwarder,
IHttpClientFactory httpClientFactory,
FastGithubConfig fastGithubConfig,
- ILogger logger)
+ ILogger logger)
{
this.httpForwarder = httpForwarder;
this.httpClientFactory = httpClientFactory;
diff --git a/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs b/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs
index a6c8c43..71b7018 100644
--- a/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs
+++ b/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs
@@ -25,24 +25,40 @@ namespace FastGithub
}
///
- /// 监听ssh
+ /// 监听http代理
///
///
- public static void ListenSsh(this KestrelServerOptions kestrel)
+ public static void ListenHttpProxy(this KestrelServerOptions kestrel)
+ {
+ var httpPort = kestrel.ApplicationServices.GetRequiredService().HttpProxyPort;
+ if (LocalMachine.CanListenTcp(httpPort) == false)
+ {
+ throw new FastGithubException("tcp端口{httpsPort}已经被其它进程占用,请在配置文件更换一个端口");
+ }
+
+ kestrel.Listen(IPAddress.Any, httpPort);
+ kestrel.GetLogger().LogInformation($"已监听tcp端口{httpPort},http代理启动完成");
+ }
+
+ ///
+ /// 监听ssh反向代理
+ ///
+ ///
+ public static void ListenSshReverseProxy(this KestrelServerOptions kestrel)
{
const int SSH_PORT = 22;
if (LocalMachine.CanListenTcp(SSH_PORT) == true)
{
- kestrel.Listen(IPAddress.Any, SSH_PORT, listen => listen.UseConnectionHandler());
+ kestrel.Listen(IPAddress.Any, SSH_PORT, listen => listen.UseConnectionHandler());
kestrel.GetLogger().LogInformation($"已监听tcp端口{SSH_PORT},github的ssh代理启动完成");
}
}
///
- /// 监听http
+ /// 监听http反向代理
///
///
- public static void ListenHttp(this KestrelServerOptions kestrel)
+ public static void ListenHttpReverseProxy(this KestrelServerOptions kestrel)
{
const int HTTP_PORT = 80;
if (LocalMachine.CanListenTcp(HTTP_PORT) == true)
@@ -53,34 +69,34 @@ namespace FastGithub
}
///
- /// 监听https
+ /// 监听https反向代理
///
///
///
- public static void ListenHttps(this KestrelServerOptions kestrel)
+ public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
{
- const int HTTPS_PORT = 443;
+ var httpsPort = kestrel.ApplicationServices.GetRequiredService().HttpsReverseProxyPort;
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.CreateCaCertIfNotExists();
certService.InstallAndTrustCaCert();
- kestrel.Listen(IPAddress.Any, HTTPS_PORT,
+ kestrel.Listen(IPAddress.Any, httpsPort,
listen => listen.UseHttps(https =>
https.ServerCertificateSelector = (ctx, domain) =>
certService.GetOrCreateServerCert(domain)));
var logger = kestrel.GetLogger();
- logger.LogInformation($"已监听tcp端口{HTTPS_PORT},https反向代理启动完成");
+ logger.LogInformation($"已监听tcp端口{httpsPort},https反向代理启动完成");
}
diff --git a/FastGithub.ReverseProxy/PortService.cs b/FastGithub.ReverseProxy/PortService.cs
new file mode 100644
index 0000000..31074f4
--- /dev/null
+++ b/FastGithub.ReverseProxy/PortService.cs
@@ -0,0 +1,48 @@
+using FastGithub.Configuration;
+using Microsoft.Extensions.Options;
+using System;
+using System.Net.Sockets;
+
+namespace FastGithub.ReverseProxy
+{
+ ///
+ /// 端口管理服务
+ ///
+ public class PortService
+ {
+ private int httpsReverseProxyPort = -1;
+
+ ///
+ /// http代理端口
+ ///
+ public int HttpProxyPort { get; }
+
+ ///
+ /// 获取https反向代理端口
+ ///
+ public int HttpsReverseProxyPort
+ {
+ get
+ {
+ if (OperatingSystem.IsWindows())
+ {
+ return 443;
+ }
+ if (this.httpsReverseProxyPort < 0)
+ {
+ this.httpsReverseProxyPort = LocalMachine.GetAvailablePort(AddressFamily.InterNetwork);
+ }
+ return this.httpsReverseProxyPort;
+ }
+ }
+
+ ///
+ /// 端口管理服务
+ ///
+ ///
+ public PortService(IOptions options)
+ {
+ this.HttpProxyPort = options.Value.HttpProxyPort;
+ }
+ }
+}
diff --git a/FastGithub.ReverseProxy/ServiceCollectionExtensions.cs b/FastGithub.ReverseProxy/ServiceCollectionExtensions.cs
index bd9598f..f265b0e 100644
--- a/FastGithub.ReverseProxy/ServiceCollectionExtensions.cs
+++ b/FastGithub.ReverseProxy/ServiceCollectionExtensions.cs
@@ -19,8 +19,10 @@ namespace FastGithub
.AddMemoryCache()
.AddHttpForwarder()
.AddSingleton()
- .AddSingleton()
- .AddSingleton();
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton();
}
}
}
diff --git a/FastGithub.ReverseProxy/GithubSshProxyHandler.cs b/FastGithub.ReverseProxy/SshReverseProxyHandler.cs
similarity index 91%
rename from FastGithub.ReverseProxy/GithubSshProxyHandler.cs
rename to FastGithub.ReverseProxy/SshReverseProxyHandler.cs
index f7b7883..d08d764 100644
--- a/FastGithub.ReverseProxy/GithubSshProxyHandler.cs
+++ b/FastGithub.ReverseProxy/SshReverseProxyHandler.cs
@@ -10,7 +10,7 @@ namespace FastGithub.ReverseProxy
///
/// github的ssh代理处理者
///
- sealed class GithubSshProxyHandler : ConnectionHandler
+ sealed class SshReverseProxyHandler : ConnectionHandler
{
private readonly IDomainResolver domainResolver;
private readonly DnsEndPoint githubSshEndPoint = new("ssh.github.com", 443);
@@ -19,7 +19,7 @@ namespace FastGithub.ReverseProxy
/// github的ssh代理处理者
///
///
- public GithubSshProxyHandler(IDomainResolver domainResolver)
+ public SshReverseProxyHandler(IDomainResolver domainResolver)
{
this.domainResolver = domainResolver;
}
diff --git a/FastGithub/Program.cs b/FastGithub/Program.cs
index f94e173..87a0fe7 100644
--- a/FastGithub/Program.cs
+++ b/FastGithub/Program.cs
@@ -52,9 +52,17 @@ namespace FastGithub
webBuilder.UseKestrel(kestrel =>
{
kestrel.NoLimit();
- kestrel.ListenHttps();
- kestrel.ListenHttp();
- kestrel.ListenSsh();
+ kestrel.ListenHttpsReverseProxy();
+
+ if (OperatingSystem.IsWindows())
+ {
+ kestrel.ListenHttpReverseProxy();
+ kestrel.ListenSshReverseProxy();
+ }
+ else
+ {
+ kestrel.ListenHttpProxy();
+ }
});
webBuilder.UseSerilog((hosting, logger) =>
{
diff --git a/FastGithub/Startup.cs b/FastGithub/Startup.cs
index d4b0d83..4af2cdf 100644
--- a/FastGithub/Startup.cs
+++ b/FastGithub/Startup.cs
@@ -1,9 +1,9 @@
using FastGithub.Configuration;
+using FastGithub.ReverseProxy;
using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using System.IO;
+using System;
using System.Threading.Tasks;
namespace FastGithub
@@ -34,10 +34,14 @@ namespace FastGithub
services.AddConfiguration();
services.AddDomainResolve();
- services.AddDnsServer();
services.AddHttpClient();
services.AddReverseProxy();
services.AddHostedService();
+
+ if (OperatingSystem.IsWindows())
+ {
+ services.AddDnsPoisoning();
+ }
}
///
@@ -46,26 +50,31 @@ namespace FastGithub
///
public void Configure(IApplicationBuilder app)
{
- app.UseRequestLogging();
- app.UseDnsOverHttps();
- app.UseReverseProxy();
-
- app.UseRouting();
- app.UseEndpoints(endpoint =>
+ if (OperatingSystem.IsWindows())
{
- 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 =>
+ 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();
+ app.MapWhen(context => context.Connection.LocalPort == portService.HttpProxyPort, appBuilder =>
+ {
+ appBuilder.UseHttpProxy();
});
- });
+
+ app.MapWhen(context => context.Connection.LocalPort != portService.HttpProxyPort, appBuilder =>
+ {
+ appBuilder.UseRequestLogging();
+ appBuilder.UseHttpReverseProxy();
+ });
+ }
}
}
}
diff --git a/FastGithub/appsettings.json b/FastGithub/appsettings.json
index 15aab34..7efe097 100644
--- a/FastGithub/appsettings.json
+++ b/FastGithub/appsettings.json
@@ -1,6 +1,7 @@
{
// 新增的子配置文件appsettings.*.json,重启应用程序才生效
"FastGithub": {
+ "HttpProxyPort": 2222, // http代理端口,非windows才使用
"FallbackDns": [ // 用于解析不在DomainConfigs的域名
{
"IPAddress": "114.114.114.114",