Browse Source

合并http代理

陈国伟 3 năm trước cách đây
mục cha
commit
9c2774dfa4

+ 5 - 0
FastGithub.Configuration/FastGithubOptions.cs

@@ -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>

+ 1 - 1
FastGithub.Configuration/LocalMachine.cs

@@ -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 - 0
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
+{
+    /// <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);
+            }
+        }
+    }
+}

+ 0 - 23
FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs

@@ -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));
-        }
-    }
-}

+ 0 - 129
FastGithub.Dns/DnsOverHttpsMiddleware.cs

@@ -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;
-        }
-    }
-}

+ 0 - 107
FastGithub.Dns/DnsOverUdpHostedService.cs

@@ -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);
-        }
-    }
-}

+ 0 - 141
FastGithub.Dns/DnsOverUdpServer.cs

@@ -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);
-            } 
-        }
-    }
-}

+ 4 - 5
FastGithub.Dns/DnsPoisoningServer.cs

@@ -16,19 +16,19 @@ 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";
         private readonly FastGithubConfig fastGithubConfig;
         private readonly FastGithubConfig fastGithubConfig;
         private readonly ILogger<DnsPoisoningServer> logger;
         private readonly ILogger<DnsPoisoningServer> logger;
-        private readonly TimeSpan ttl = TimeSpan.FromSeconds(10d); 
+        private readonly TimeSpan ttl = TimeSpan.FromSeconds(10d);
 
 
         /// <summary>
         /// <summary>
         /// 刷新DNS缓存
         /// 刷新DNS缓存
-        /// </summary>
-        [SupportedOSPlatform("windows")]
+        /// </summary>    
         [DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)]
         [DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)]
-        private static extern void DnsFlushResolverCache();  
+        private static extern void DnsFlushResolverCache();
 
 
         /// <summary>
         /// <summary>
         /// dns投毒后台服务
         /// dns投毒后台服务
@@ -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);

+ 0 - 37
FastGithub.Dns/RemoteEndPointRequest.cs

@@ -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);
-        }
-    }
-}

+ 0 - 80
FastGithub.Dns/RequestResolver.cs

@@ -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;
-        }
-    }
-}

+ 6 - 7
FastGithub.Dns/ServiceCollectionExtensions.cs

@@ -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)
-        {
-            services.TryAddSingleton<RequestResolver>();
-            services.TryAddSingleton<DnsOverUdpServer>();
+        [SupportedOSPlatform("windows")]
+        public static IServiceCollection AddDnsPoisoning(this IServiceCollection services)
+        { 
             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>();
         }
         }
     }
     }
 }
 }

+ 0 - 89
FastGithub.Dns/SystemDnsUtil.cs

@@ -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();
-        }
-    }
-}

+ 13 - 2
FastGithub.ReverseProxy/ApplicationBuilderExtensions.cs

@@ -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 - 0
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
+{
+    /// <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);
+        }
+    }
+}

+ 4 - 4
FastGithub.ReverseProxy/ReverseProxyMiddleware.cs → FastGithub.ReverseProxy/HttpReverseProxyMiddleware.cs

@@ -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;

+ 29 - 13
FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs

@@ -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 - 0
FastGithub.ReverseProxy/PortService.cs

@@ -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;
+        }
+    }
+}

+ 4 - 2
FastGithub.ReverseProxy/ServiceCollectionExtensions.cs

@@ -19,8 +19,10 @@ namespace FastGithub
                 .AddMemoryCache()
                 .AddMemoryCache()
                 .AddHttpForwarder()
                 .AddHttpForwarder()
                 .AddSingleton<CertService>()
                 .AddSingleton<CertService>()
-                .AddSingleton<RequestLoggingMiddleware>()
-                .AddSingleton<ReverseProxyMiddleware>();
+                .AddSingleton<PortService>()
+                .AddSingleton<HttpProxyMiddleware>()
+                .AddSingleton<RequestLoggingMiddleware>()                
+                .AddSingleton<HttpReverseProxyMiddleware>();
         }
         }
     }
     }
 }
 }

+ 2 - 2
FastGithub.ReverseProxy/GithubSshProxyHandler.cs → FastGithub.ReverseProxy/SshReverseProxyHandler.cs

@@ -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;
         }
         }

+ 11 - 3
FastGithub/Program.cs

@@ -52,9 +52,17 @@ namespace FastGithub
                     webBuilder.UseKestrel(kestrel =>
                     webBuilder.UseKestrel(kestrel =>
                     {
                     {
                         kestrel.NoLimit();
                         kestrel.NoLimit();
-                        kestrel.ListenHttps(); 
-                        kestrel.ListenHttp();
-                        kestrel.ListenSsh();                                   
+                        kestrel.ListenHttpsReverseProxy();
+
+                        if (OperatingSystem.IsWindows())
+                        {
+                            kestrel.ListenHttpReverseProxy();
+                            kestrel.ListenSshReverseProxy();
+                        }
+                        else
+                        {
+                            kestrel.ListenHttpProxy();
+                        }
                     });
                     });
                     webBuilder.UseSerilog((hosting, logger) =>
                     webBuilder.UseSerilog((hosting, logger) =>
                     {
                     {

+ 27 - 18
FastGithub/Startup.cs

@@ -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>
@@ -46,26 +50,31 @@ namespace FastGithub
         /// <param name="app"></param>
         /// <param name="app"></param>
         public void Configure(IApplicationBuilder app)
         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");
                     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 - 0
FastGithub/appsettings.json

@@ -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",