diff --git a/FastGithub.HttpServer/HttpsReverseProxyPort.cs b/FastGithub.Configuration/HttpsReverseProxyPort.cs similarity index 65% rename from FastGithub.HttpServer/HttpsReverseProxyPort.cs rename to FastGithub.Configuration/HttpsReverseProxyPort.cs index f3d33db..eae78f8 100644 --- a/FastGithub.HttpServer/HttpsReverseProxyPort.cs +++ b/FastGithub.Configuration/HttpsReverseProxyPort.cs @@ -1,21 +1,32 @@ -using FastGithub.Configuration; -using System; +using System; using System.Collections.Generic; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -namespace FastGithub.HttpServer +namespace FastGithub.Configuration { /// /// https反向代理端口 /// - static class HttpsReverseProxyPort + public static class HttpsReverseProxyPort { /// /// 获取端口值 /// - public static int Value { get; } = OperatingSystem.IsWindows() ? 443 : GetAvailableTcpPort(AddressFamily.InterNetwork); + public static int Value { get; } = GetAvailableTcpPort(AddressFamily.InterNetwork); + + /// + /// 获取可用的随机Tcp端口 + /// + /// + /// + private static int GetAvailableTcpPort(AddressFamily addressFamily) + { + return OperatingSystem.IsWindows() + ? GetAvailableTcpPort(addressFamily, 443) + : GetAvailableTcpPort(addressFamily, 12345); + } /// /// 获取可用的随机Tcp端口 @@ -23,7 +34,7 @@ namespace FastGithub.HttpServer /// /// 最小值 /// - private static int GetAvailableTcpPort(AddressFamily addressFamily, int min = 12345) + private static int GetAvailableTcpPort(AddressFamily addressFamily, int min) { var hashSet = new HashSet(); var tcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners(); diff --git a/FastGithub.HttpServer/KestrelServerOptionsExtensions.cs b/FastGithub.HttpServer/KestrelServerOptionsExtensions.cs index fb9e3d1..312c1e1 100644 --- a/FastGithub.HttpServer/KestrelServerOptionsExtensions.cs +++ b/FastGithub.HttpServer/KestrelServerOptionsExtensions.cs @@ -86,11 +86,6 @@ namespace FastGithub public static int ListenHttpsReverseProxy(this KestrelServerOptions kestrel) { var httpsPort = HttpsReverseProxyPort.Value; - if (OperatingSystem.IsWindows()) - { - TcpTable.KillPortOwner(httpsPort); - } - if (CanListenTcp(httpsPort) == false) { throw new FastGithubException($"tcp端口{httpsPort}已经被其它进程占用"); @@ -105,7 +100,7 @@ namespace FastGithub https.ServerCertificateSelector = (ctx, domain) => certService.GetOrCreateServerCert(domain))); - if (httpsPort == 443) + if (OperatingSystem.IsWindows()) { var logger = kestrel.GetLogger(); logger.LogInformation($"已监听https://{IPAddress.Loopback}:{httpsPort},https反向代理服务启动完成"); diff --git a/FastGithub.HttpServer/TcpTable.cs b/FastGithub.HttpServer/TcpTable.cs deleted file mode 100644 index 9737305..0000000 --- a/FastGithub.HttpServer/TcpTable.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Diagnostics; -using System.Net; -using System.Net.Sockets; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace FastGithub.HttpServer -{ - /// - /// windows iphlpapi - /// - [SupportedOSPlatform("windows")] - unsafe static class TcpTable - { - private const int ERROR_INSUFFICIENT_BUFFER = 122; - - [DllImport("iphlpapi.dll", SetLastError = true)] - private static extern uint GetExtendedTcpTable(void* pTcpTable, ref int pdwSize, bool bOrder, AddressFamily ulAf, TCP_TABLE_CLASS tableClass, uint reserved = 0); - - - /// - /// 杀死占用进程 - /// - /// - /// - public static bool KillPortOwner(int port) - { - if (TryGetOwnerProcessId(port, out var pid) == false) - { - return true; - } - - try - { - var proess = Process.GetProcessById(pid); - proess.Kill(); - return proess.WaitForExit(1000); - } - catch (ArgumentException) - { - return true; - } - catch (Exception) - { - return false; - } - } - - /// - /// 获取tcp端口的占用进程id - /// - /// - /// - /// - public static bool TryGetOwnerProcessId(int port, out int processId) - { - processId = 0; - var pdwSize = 0; - var result = GetExtendedTcpTable(null, ref pdwSize, false, AddressFamily.InterNetwork, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_LISTENER); - if (result != ERROR_INSUFFICIENT_BUFFER) - { - return false; - } - - var buffer = new byte[pdwSize]; - fixed (byte* pTcpTable = &buffer[0]) - { - result = GetExtendedTcpTable(pTcpTable, ref pdwSize, false, AddressFamily.InterNetwork, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_LISTENER); - if (result != 0) - { - return false; - } - - var prt = new IntPtr(pTcpTable); - var table = Marshal.PtrToStructure(prt); - prt += sizeof(int); - for (var i = 0; i < table.dwNumEntries; i++) - { - var row = Marshal.PtrToStructure(prt); - if (row.LocalPort == port) - { - processId = row.ProcessId; - return true; - } - - prt += Marshal.SizeOf(); - } - } - - return false; - } - - - private enum TCP_TABLE_CLASS - { - TCP_TABLE_BASIC_LISTENER, - TCP_TABLE_BASIC_CONNECTIONS, - TCP_TABLE_BASIC_ALL, - TCP_TABLE_OWNER_PID_LISTENER, - TCP_TABLE_OWNER_PID_CONNECTIONS, - TCP_TABLE_OWNER_PID_ALL, - TCP_TABLE_OWNER_MODULE_LISTENER, - TCP_TABLE_OWNER_MODULE_CONNECTIONS, - TCP_TABLE_OWNER_MODULE_ALL - } - - [StructLayout(LayoutKind.Sequential)] - private struct MIB_TCPTABLE_OWNER_PID - { - public uint dwNumEntries; - } - - [StructLayout(LayoutKind.Sequential)] - private struct MIB_TCPROW_OWNER_PID - { - public uint state; - - public uint localAddr; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] localPort; - - public uint remoteAddr; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] remotePort; - - public int owningPid; - - public int ProcessId => owningPid; - - public IPAddress LocalAddress => new(localAddr); - - public ushort LocalPort => BinaryPrimitives.ReadUInt16BigEndian(this.localPort); - } - } -} diff --git a/FastGithub.Dns/DnsInterceptHostedService.cs b/FastGithub.PacketIntercept/DnsInterceptHostedService.cs similarity index 100% rename from FastGithub.Dns/DnsInterceptHostedService.cs rename to FastGithub.PacketIntercept/DnsInterceptHostedService.cs diff --git a/FastGithub.Dns/DnsInterceptor.cs b/FastGithub.PacketIntercept/DnsInterceptor.cs similarity index 99% rename from FastGithub.Dns/DnsInterceptor.cs rename to FastGithub.PacketIntercept/DnsInterceptor.cs index 7244333..64df7af 100644 --- a/FastGithub.Dns/DnsInterceptor.cs +++ b/FastGithub.PacketIntercept/DnsInterceptor.cs @@ -154,6 +154,7 @@ namespace FastGithub.Dns packet.UdpHeader->Length = BinaryPrimitives.ReverseEndianness((ushort)(sizeof(UdpHeader) + responsePayload.Length)); // 反转方向 + winDivertAddress.Impostor = true; if (winDivertAddress.Direction == WinDivertDirection.Inbound) { winDivertAddress.Direction = WinDivertDirection.Outbound; @@ -162,7 +163,7 @@ namespace FastGithub.Dns { winDivertAddress.Direction = WinDivertDirection.Inbound; } - + WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All); this.logger.LogInformation($"已拦截dns查询{domain}并伪造解析结果为{IPAddress.Loopback}"); } diff --git a/FastGithub.Dns/FastGithub.Dns.csproj b/FastGithub.PacketIntercept/FastGithub.PacketIntercept.csproj similarity index 90% rename from FastGithub.Dns/FastGithub.Dns.csproj rename to FastGithub.PacketIntercept/FastGithub.PacketIntercept.csproj index 08a71e8..cbc4b61 100644 --- a/FastGithub.Dns/FastGithub.Dns.csproj +++ b/FastGithub.PacketIntercept/FastGithub.PacketIntercept.csproj @@ -9,6 +9,7 @@ + diff --git a/FastGithub.Dns/HostsConflictSolver.cs b/FastGithub.PacketIntercept/HostsConflictSolver.cs similarity index 100% rename from FastGithub.Dns/HostsConflictSolver.cs rename to FastGithub.PacketIntercept/HostsConflictSolver.cs diff --git a/FastGithub.PacketIntercept/HttpsInterceptHostedService.cs b/FastGithub.PacketIntercept/HttpsInterceptHostedService.cs new file mode 100644 index 0000000..2200556 --- /dev/null +++ b/FastGithub.PacketIntercept/HttpsInterceptHostedService.cs @@ -0,0 +1,37 @@ +using FastGithub.Configuration; +using Microsoft.Extensions.Hosting; +using System.Runtime.Versioning; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.Dns +{ + /// + /// https拦截后台服务 + /// + [SupportedOSPlatform("windows")] + sealed class HttpsInterceptHostedService : BackgroundService + { + private readonly HttpsInterceptor httpsInterceptor; + + /// + /// https拦截后台服务 + /// + /// + public HttpsInterceptHostedService(HttpsInterceptor httpsInterceptor) + { + this.httpsInterceptor = httpsInterceptor; + } + + /// + /// https后台 + /// + /// + /// + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await Task.Yield(); + this.httpsInterceptor.Intercept(stoppingToken); + } + } +} diff --git a/FastGithub.PacketIntercept/HttpsInterceptor.cs b/FastGithub.PacketIntercept/HttpsInterceptor.cs new file mode 100644 index 0000000..8bbe9ec --- /dev/null +++ b/FastGithub.PacketIntercept/HttpsInterceptor.cs @@ -0,0 +1,95 @@ +using FastGithub.Configuration; +using Microsoft.Extensions.Logging; +using System; +using System.Buffers.Binary; +using System.Runtime.Versioning; +using System.Threading; +using WinDivertSharp; + +namespace FastGithub.Dns +{ + /// + /// https拦截器 + /// + [SupportedOSPlatform("windows")] + sealed class HttpsInterceptor + { + private readonly ILogger logger; + private readonly ushort https443Port = BinaryPrimitives.ReverseEndianness((ushort)443); + private readonly ushort httpReverseProxyPort = BinaryPrimitives.ReverseEndianness((ushort)HttpsReverseProxyPort.Value); + + /// + /// https拦截器 + /// + /// + public HttpsInterceptor(ILogger logger) + { + this.logger = logger; + } + + /// + /// 拦截443端口的数据包 + /// + /// + public void Intercept(CancellationToken cancellationToken) + { + if (HttpsReverseProxyPort.Value == 443) + { + return; + } + + var filter = $"loopback and (tcp.DstPort == 443 or tcp.SrcPort == {HttpsReverseProxyPort.Value})"; + var handle = WinDivert.WinDivertOpen(filter, WinDivertLayer.Network, 0, WinDivertOpenFlags.None); + if (handle == IntPtr.Zero) + { + return; + } + + cancellationToken.Register(hwnd => WinDivert.WinDivertClose((IntPtr)hwnd!), handle); + + var packetLength = 0U; + using var winDivertBuffer = new WinDivertBuffer(); + var winDivertAddress = new WinDivertAddress(); + + while (cancellationToken.IsCancellationRequested == false) + { + if (WinDivert.WinDivertRecv(handle, winDivertBuffer, ref winDivertAddress, ref packetLength)) + { + try + { + this.ModifyHttpsPacket(winDivertBuffer, ref winDivertAddress, ref packetLength); + } + catch (Exception ex) + { + this.logger.LogWarning(ex.Message); + } + finally + { + WinDivert.WinDivertSend(handle, winDivertBuffer, packetLength, ref winDivertAddress); + } + } + } + } + + /// + /// 443端口转发到https反向代理端口 + /// + /// + /// + /// + unsafe private void ModifyHttpsPacket(WinDivertBuffer winDivertBuffer, ref WinDivertAddress winDivertAddress, ref uint packetLength) + { + var packet = WinDivert.WinDivertHelperParsePacket(winDivertBuffer, packetLength); + if (packet.TcpHeader->DstPort == https443Port) + { + packet.TcpHeader->DstPort = this.httpReverseProxyPort; + } + else + { + packet.TcpHeader->SrcPort = https443Port; + } + winDivertAddress.Impostor = true; + WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All); + } + } +} diff --git a/FastGithub.Dns/IConflictSolver.cs b/FastGithub.PacketIntercept/IConflictSolver.cs similarity index 100% rename from FastGithub.Dns/IConflictSolver.cs rename to FastGithub.PacketIntercept/IConflictSolver.cs diff --git a/FastGithub.Dns/ProxyConflictSolver.cs b/FastGithub.PacketIntercept/ProxyConflictSolver.cs similarity index 100% rename from FastGithub.Dns/ProxyConflictSolver.cs rename to FastGithub.PacketIntercept/ProxyConflictSolver.cs diff --git a/FastGithub.Dns/ServiceCollectionExtensions.cs b/FastGithub.PacketIntercept/ServiceCollectionExtensions.cs similarity index 67% rename from FastGithub.Dns/ServiceCollectionExtensions.cs rename to FastGithub.PacketIntercept/ServiceCollectionExtensions.cs index f4736dc..b5506ea 100644 --- a/FastGithub.Dns/ServiceCollectionExtensions.cs +++ b/FastGithub.PacketIntercept/ServiceCollectionExtensions.cs @@ -11,17 +11,19 @@ namespace FastGithub public static class ServiceCollectionExtensions { /// - /// 注册dns拦截器 + /// 注册数据包拦截器 /// /// /// [SupportedOSPlatform("windows")] - public static IServiceCollection AddDnsInterceptor(this IServiceCollection services) + public static IServiceCollection AddPacketIntercept(this IServiceCollection services) { services.TryAddSingleton(); + services.TryAddSingleton(); services.AddSingleton(); services.AddSingleton(); - return services.AddHostedService(); + services.AddHostedService(); + return services.AddHostedService(); } } } diff --git a/FastGithub.sln b/FastGithub.sln index 40a868f..7bfdb8e 100644 --- a/FastGithub.sln +++ b/FastGithub.sln @@ -1,12 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31320.298 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31612.314 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub", "FastGithub\FastGithub.csproj", "{C1099390-6103-4917-A740-A3002B542FE0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Dns", "FastGithub.Dns\FastGithub.Dns.csproj", "{43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Http", "FastGithub.Http\FastGithub.Http.csproj", "{B5DCB3E4-5094-4170-B844-6F395002CA42}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.DomainResolve", "FastGithub.DomainResolve\FastGithub.DomainResolve.csproj", "{5D26ABDD-F341-4EB7-9D08-FCB80F79B4B4}" @@ -15,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.Configuration", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.HttpServer", "FastGithub.HttpServer\FastGithub.HttpServer.csproj", "{C9807DA0-4620-445E-ABBF-57A617B8E773}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.PacketIntercept", "FastGithub.PacketIntercept\FastGithub.PacketIntercept.csproj", "{701FF90C-E651-4E0B-AE7F-84D1F17DD178}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -25,10 +25,6 @@ Global {C1099390-6103-4917-A740-A3002B542FE0}.Debug|Any CPU.Build.0 = Debug|Any CPU {C1099390-6103-4917-A740-A3002B542FE0}.Release|Any CPU.ActiveCfg = Release|Any CPU {C1099390-6103-4917-A740-A3002B542FE0}.Release|Any CPU.Build.0 = Release|Any CPU - {43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {43FF9C79-51D5-4037-AA0B-CA3006E2A7E6}.Release|Any CPU.Build.0 = Release|Any CPU {B5DCB3E4-5094-4170-B844-6F395002CA42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B5DCB3E4-5094-4170-B844-6F395002CA42}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5DCB3E4-5094-4170-B844-6F395002CA42}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -45,6 +41,10 @@ Global {C9807DA0-4620-445E-ABBF-57A617B8E773}.Debug|Any CPU.Build.0 = Debug|Any CPU {C9807DA0-4620-445E-ABBF-57A617B8E773}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9807DA0-4620-445E-ABBF-57A617B8E773}.Release|Any CPU.Build.0 = Release|Any CPU + {701FF90C-E651-4E0B-AE7F-84D1F17DD178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {701FF90C-E651-4E0B-AE7F-84D1F17DD178}.Debug|Any CPU.Build.0 = Debug|Any CPU + {701FF90C-E651-4E0B-AE7F-84D1F17DD178}.Release|Any CPU.ActiveCfg = Release|Any CPU + {701FF90C-E651-4E0B-AE7F-84D1F17DD178}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FastGithub/FastGithub.csproj b/FastGithub/FastGithub.csproj index b3a7101..9b803de 100644 --- a/FastGithub/FastGithub.csproj +++ b/FastGithub/FastGithub.csproj @@ -12,9 +12,9 @@ - + diff --git a/FastGithub/Startup.cs b/FastGithub/Startup.cs index 9698b93..4bd6efd 100644 --- a/FastGithub/Startup.cs +++ b/FastGithub/Startup.cs @@ -40,7 +40,7 @@ namespace FastGithub if (OperatingSystem.IsWindows()) { - services.AddDnsInterceptor(); + services.AddPacketIntercept(); } }