From 0f94f118ca7517d26d3d64864fa2a01c316e54b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=80=81=E4=B9=9D?= <366193849@qq.com>
Date: Wed, 25 Aug 2021 20:38:44 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0DnsOverHttps=E6=9C=8D?=
 =?UTF-8?q?=E5=8A=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
 ...nsOverHttpsApplicationBuilderExtensions.cs |  23 ++++
 FastGithub.Dns/DnsOverHttpsMiddleware.cs      | 103 ++++++++++++++++++
 FastGithub.Dns/FastGithub.Dns.csproj          |   1 +
 FastGithub.Dns/ServiceCollectionExtensions.cs |   1 +
 FastGithub/Startup.cs                         |   1 +
 5 files changed, 129 insertions(+)
 create mode 100644 FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs
 create mode 100644 FastGithub.Dns/DnsOverHttpsMiddleware.cs
diff --git a/FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs b/FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs
new file mode 100644
index 0000000..9255fd8
--- /dev/null
+++ b/FastGithub.Dns/DnsOverHttpsApplicationBuilderExtensions.cs
@@ -0,0 +1,23 @@
+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
new file mode 100644
index 0000000..e7d9b35
--- /dev/null
+++ b/FastGithub.Dns/DnsOverHttpsMiddleware.cs
@@ -0,0 +1,103 @@
+using DNS.Protocol;
+using Microsoft.AspNetCore.Http;
+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;
+
+        /// 
+        /// DoH中间件
+        /// 
+        /// 
+        public DnsOverHttpsMiddleware(RequestResolver requestResolver)
+        {
+            this.requestResolver = requestResolver;
+        }
+
+        /// 
+        /// 执行请求
+        /// 
+        /// 
+        /// 
+        /// 
+        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
+        {
+            try
+            {
+                var request = await ParseDnsRequestAsync(context.Request);
+                if (request == null)
+                {
+                    await next(context);
+                }
+                else
+                {
+                    var remoteIPAddress = context.Connection.RemoteIpAddress ?? IPAddress.Loopback;
+                    var remoteEndPoint = new IPEndPoint(remoteIPAddress, context.Connection.RemotePort);
+                    var remoteEndPointRequest = new RemoteEndPointRequest(request, remoteEndPoint);
+                    var response = await this.requestResolver.Resolve(remoteEndPointRequest);
+
+                    context.Response.ContentType = MEDIA_TYPE;
+                    await context.Response.BodyWriter.WriteAsync(response.ToArray());
+                }
+            }
+            catch (Exception)
+            {
+                await next(context);
+            }
+        }
+
+        /// 
+        /// 解析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/FastGithub.Dns.csproj b/FastGithub.Dns/FastGithub.Dns.csproj
index ea1d4dc..4bc4ac2 100644
--- a/FastGithub.Dns/FastGithub.Dns.csproj
+++ b/FastGithub.Dns/FastGithub.Dns.csproj
@@ -5,6 +5,7 @@
 	
 
 	
+		
 		
 		
 	
diff --git a/FastGithub.Dns/ServiceCollectionExtensions.cs b/FastGithub.Dns/ServiceCollectionExtensions.cs
index 3f74afc..0d7b51a 100644
--- a/FastGithub.Dns/ServiceCollectionExtensions.cs
+++ b/FastGithub.Dns/ServiceCollectionExtensions.cs
@@ -18,6 +18,7 @@ namespace FastGithub
         {
             services.TryAddSingleton();
             services.TryAddSingleton();
+            services.TryAddSingleton();
             services.AddSingleton();
             services.AddSingleton();
             return services.AddHostedService();
diff --git a/FastGithub/Startup.cs b/FastGithub/Startup.cs
index 2d14eb6..bd0d3e6 100644
--- a/FastGithub/Startup.cs
+++ b/FastGithub/Startup.cs
@@ -45,6 +45,7 @@ namespace FastGithub
         public void Configure(IApplicationBuilder app)
         {
             app.UseRequestLogging();
+            app.UseDnsOverHttps();
             app.UseReverseProxy();
             app.UseRouting();
             app.UseEndpoints(endpoints =>