diff --git a/FastGithub.Dns/DnsOverUdpHostedService.cs b/FastGithub.Dns/DnsOverUdpHostedService.cs index 7d52c97..4328e3a 100644 --- a/FastGithub.Dns/DnsOverUdpHostedService.cs +++ b/FastGithub.Dns/DnsOverUdpHostedService.cs @@ -15,6 +15,7 @@ namespace FastGithub.Dns sealed class DnsOverUdpHostedService : BackgroundService { private readonly DnsOverUdpServer dnsOverUdpServer; + private readonly DnsPoisoningServer dnsPoisoningServer; private readonly IEnumerable conflictValidators; private readonly ILogger logger; @@ -22,14 +23,17 @@ namespace FastGithub.Dns /// dns后台服务 /// /// + /// /// /// public DnsOverUdpHostedService( DnsOverUdpServer dnsOverUdpServer, + DnsPoisoningServer dnsPoisoningServer, IEnumerable conflictValidators, ILogger logger) { this.dnsOverUdpServer = dnsOverUdpServer; + this.dnsPoisoningServer = dnsPoisoningServer; this.conflictValidators = conflictValidators; this.logger = logger; } @@ -41,20 +45,23 @@ namespace FastGithub.Dns /// public override async Task StartAsync(CancellationToken cancellationToken) { - try + if (OperatingSystem.IsWindows() == false) { - 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()); + 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) @@ -70,9 +77,17 @@ namespace FastGithub.Dns /// /// /// - protected override Task ExecuteAsync(CancellationToken stoppingToken) + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - return this.dnsOverUdpServer.HandleAsync(stoppingToken); + if (OperatingSystem.IsWindows()) + { + await Task.Yield(); + this.dnsPoisoningServer.DnsPoisoning(stoppingToken); + } + else + { + await this.dnsOverUdpServer.HandleAsync(stoppingToken); + } } /// @@ -82,7 +97,10 @@ namespace FastGithub.Dns /// public override Task StopAsync(CancellationToken cancellationToken) { - this.dnsOverUdpServer.Dispose(); + if (OperatingSystem.IsWindows() == false) + { + this.dnsOverUdpServer.Dispose(); + } return base.StopAsync(cancellationToken); } } diff --git a/FastGithub.Dns/DnsOverUdpServer.cs b/FastGithub.Dns/DnsOverUdpServer.cs index 2102cbb..d044004 100644 --- a/FastGithub.Dns/DnsOverUdpServer.cs +++ b/FastGithub.Dns/DnsOverUdpServer.cs @@ -47,11 +47,6 @@ namespace FastGithub.Dns /// public void Listen(IPAddress address, int port) { - if (OperatingSystem.IsWindows()) - { - UdpTable.KillPortOwner(port); - } - if (LocalMachine.CanListenUdp(port) == false) { throw new FastGithubException($"udp端口{port}已经被其它进程占用"); diff --git a/FastGithub.Dns/DnsPoisoningServer.cs b/FastGithub.Dns/DnsPoisoningServer.cs new file mode 100644 index 0000000..b8c3494 --- /dev/null +++ b/FastGithub.Dns/DnsPoisoningServer.cs @@ -0,0 +1,123 @@ +using DNS.Protocol; +using DNS.Protocol.ResourceRecords; +using FastGithub.Configuration; +using Microsoft.Extensions.Logging; +using PacketDotNet; +using System; +using System.Linq; +using System.Net; +using System.Runtime.Versioning; +using System.Threading; +using WinDivertSharp; + +namespace FastGithub.Dns +{ + /// + /// dns投毒服务 + /// + 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); + + /// + /// dns投毒后台服务 + /// + /// + /// + public DnsPoisoningServer( + FastGithubConfig fastGithubConfig, + ILogger logger) + { + this.fastGithubConfig = fastGithubConfig; + this.logger = logger; + } + + /// + /// DNS投毒 + /// + /// + [SupportedOSPlatform("windows")] + public void DnsPoisoning(CancellationToken cancellationToken) + { + var handle = WinDivert.WinDivertOpen(DNS_FILTER, WinDivertLayer.Network, 0, WinDivertOpenFlags.None); + if (handle == IntPtr.Zero) + { + return; + } + + var packetLength = 0U; + var packetBuffer = new byte[ushort.MaxValue]; + using var winDivertBuffer = new WinDivertBuffer(packetBuffer); + var winDivertAddress = new WinDivertAddress(); + + SystemDnsUtil.FlushResolverCache(); + while (cancellationToken.IsCancellationRequested == false) + { + if (WinDivert.WinDivertRecv(handle, winDivertBuffer, ref winDivertAddress, ref packetLength)) + { + try + { + this.ProcessDnsPacket(packetBuffer, ref packetLength); + } + catch (Exception ex) + { + this.logger.LogWarning(ex.Message); + } + + WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All); + WinDivert.WinDivertSend(handle, winDivertBuffer, packetLength, ref winDivertAddress); + } + } + + WinDivert.WinDivertClose(handle); + } + + /// + /// 处理DNS数据包 + /// + /// + private void ProcessDnsPacket(byte[] packetBuffer, ref uint packetLength) + { + var packetData = packetBuffer.AsSpan(0, (int)packetLength).ToArray(); + var packet = Packet.ParsePacket(LinkLayers.Raw, packetData); + var ipPacket = (IPPacket)packet.PayloadPacket; + var udpPacket = (UdpPacket)ipPacket.PayloadPacket; + + var request = Request.FromArray(udpPacket.PayloadData); + var question = request.Questions.FirstOrDefault(); + if (question == null || question.Type != RecordType.A) + { + return; + } + + var domain = question.Name; + if (this.fastGithubConfig.IsMatch(domain.ToString()) == false) + { + return; + } + + // 反转ip + var sourAddress = ipPacket.SourceAddress; + ipPacket.SourceAddress = ipPacket.DestinationAddress; + ipPacket.DestinationAddress = sourAddress; + + // 反转端口 + var sourPort = udpPacket.SourcePort; + udpPacket.SourcePort = udpPacket.DestinationPort; + udpPacket.DestinationPort = sourPort; + + // 设置dns响应 + var response = Response.FromRequest(request); + var record = new IPAddressResourceRecord(domain, IPAddress.Loopback, this.ttl); + response.AnswerRecords.Add(record); + udpPacket.PayloadData = response.ToArray(); + + // 修改数据内容和数据长度 + packet.Bytes.CopyTo(packetBuffer, 0); + packetLength = (uint)packet.Bytes.Length; + } + } +} diff --git a/FastGithub.Dns/FastGithub.Dns.csproj b/FastGithub.Dns/FastGithub.Dns.csproj index 4bc4ac2..a259d36 100644 --- a/FastGithub.Dns/FastGithub.Dns.csproj +++ b/FastGithub.Dns/FastGithub.Dns.csproj @@ -8,6 +8,8 @@ + + diff --git a/FastGithub.Dns/ServiceCollectionExtensions.cs b/FastGithub.Dns/ServiceCollectionExtensions.cs index 1205a86..21c27d2 100644 --- a/FastGithub.Dns/ServiceCollectionExtensions.cs +++ b/FastGithub.Dns/ServiceCollectionExtensions.cs @@ -18,7 +18,8 @@ namespace FastGithub { services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); services.AddSingleton(); services.AddSingleton(); return services.AddHostedService(); diff --git a/FastGithub.Dns/UdpTable.cs b/FastGithub.Dns/UdpTable.cs deleted file mode 100644 index 97b3f29..0000000 --- a/FastGithub.Dns/UdpTable.cs +++ /dev/null @@ -1,126 +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.Dns -{ - /// - /// windows iphlpapi - /// - [SupportedOSPlatform("windows")] - unsafe static class UdpTable - { - private const int ERROR_INSUFFICIENT_BUFFER = 122; - - [DllImport("iphlpapi.dll", SetLastError = true)] - private static extern uint GetExtendedUdpTable(void* pUdpTable, ref int pdwSize, bool bOrder, AddressFamily ulAf, UDP_TABLE_CLASS tableClass, uint reserved = 0); - - - /// - /// 杀死占用进程 - /// - /// - /// - 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; - } - } - - /// - /// 获取udp端口的占用进程id - /// - /// - /// - /// - public static bool TryGetOwnerProcessId(int port, out int processId) - { - processId = 0; - var pdwSize = 0; - var result = GetExtendedUdpTable(null, ref pdwSize, false, AddressFamily.InterNetwork, UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID); - if (result != ERROR_INSUFFICIENT_BUFFER) - { - return false; - } - - var buffer = new byte[pdwSize]; - fixed (byte* pUdpTable = &buffer[0]) - { - result = GetExtendedUdpTable(pUdpTable, ref pdwSize, false, AddressFamily.InterNetwork, UDP_TABLE_CLASS.UDP_TABLE_OWNER_PID); - if (result != 0) - { - return false; - } - - var prt = new IntPtr(pUdpTable); - 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 UDP_TABLE_CLASS - { - UDP_TABLE_BASIC, - UDP_TABLE_OWNER_PID, - UDP_TABLE_OWNER_MODULE - } - - [StructLayout(LayoutKind.Sequential)] - private struct MIB_UDPTABLE_OWNER_PID - { - public uint dwNumEntries; - } - - [StructLayout(LayoutKind.Sequential)] - private struct MIB_UDPROW_OWNER_PID - { - public uint localAddr; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] localPort; - - public int owningPid; - - public int ProcessId => owningPid; - - public IPAddress LocalAddress => new(localAddr); - - public ushort LocalPort => BinaryPrimitives.ReadUInt16BigEndian(this.localPort); - } - } -}