diff --git a/DNS/Client/DnsClient.cs b/DNS/Client/DnsClient.cs index 2a7a7a9..82f6eeb 100644 --- a/DNS/Client/DnsClient.cs +++ b/DNS/Client/DnsClient.cs @@ -26,6 +26,9 @@ public DnsClient(string ip, int port = DEFAULT_PORT) : public DnsClient(IRequestResolver resolver) { this.resolver = resolver; } + public DnsClient(Uri uri) : + this(new HttpsRequestResolver(uri)) + { } public ClientRequest FromArray(byte[] message) { Request request = Request.FromArray(message); diff --git a/DNS/Client/RequestResolver/HttpsRequestResolver.cs b/DNS/Client/RequestResolver/HttpsRequestResolver.cs new file mode 100644 index 0000000..282ea57 --- /dev/null +++ b/DNS/Client/RequestResolver/HttpsRequestResolver.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Net.Http; +using System.Net.Http.Headers; +using DNS.Protocol; +using DNS.Protocol.Utils; + +namespace DNS.Client.RequestResolver { + public class HttpsRequestResolver : IRequestResolver { + private int timeout; + private IRequestResolver fallback; + private HttpClient httpClient; + private void SetUpClient(Uri uri) + { + httpClient = new HttpClient() + { + BaseAddress = uri, + Timeout = TimeSpan.FromMilliseconds(timeout) + //DefaultRequestVersion = new Version(2, 0), + }; + httpClient.DefaultRequestHeaders.Add("Accept", "application/dns-message"); + } + public HttpsRequestResolver(Uri uri, IRequestResolver fallback, int timeout = 5000) { + this.fallback = fallback; + this.timeout = timeout; + SetUpClient(uri); + } + + public HttpsRequestResolver(Uri uri, int timeout = 5000) { + this.fallback = new NullRequestResolver(); + this.timeout = timeout; + SetUpClient(uri); + } + + public async Task Resolve(IRequest request, CancellationToken cancellationToken = default(CancellationToken)) { + var httpRequest = new HttpRequestMessage(HttpMethod.Post, "") + { + Content = new ByteArrayContent(request.ToArray(), 0, request.Size) + }; + + httpRequest.Content.Headers.ContentType = new MediaTypeHeaderValue("application/dns-message"); + var httpResponse = await httpClient.SendAsync(httpRequest).WithCancellationTimeout(TimeSpan.FromMilliseconds(timeout), cancellationToken).ConfigureAwait(false); + Byte[] buffer = await httpResponse.Content.ReadAsByteArrayAsync(); + Response response = Response.FromArray(buffer); + + if (response.Truncated) + { + return await fallback.Resolve(request, cancellationToken).ConfigureAwait(false); + } + + return new ClientResponse(request, response, buffer); + } + } +} diff --git a/DNS/Server/DnsServer.cs b/DNS/Server/DnsServer.cs index efd997b..172fa63 100644 --- a/DNS/Server/DnsServer.cs +++ b/DNS/Server/DnsServer.cs @@ -35,6 +35,10 @@ public DnsServer(IRequestResolver resolver, IPAddress endServer, int port = DEFA public DnsServer(IRequestResolver resolver, string endServer, int port = DEFAULT_PORT) : this(resolver, IPAddress.Parse(endServer), port) {} + public DnsServer(IRequestResolver resolver, Uri uri) : + this(new FallbackRequestResolver(resolver, new HttpsRequestResolver(uri))) + { } + public DnsServer(IPEndPoint endServer) : this(new UdpRequestResolver(endServer)) {} @@ -47,6 +51,9 @@ public DnsServer(string endServer, int port = DEFAULT_PORT) : public DnsServer(IRequestResolver resolver) { this.resolver = resolver; } + public DnsServer(Uri uri) : + this(new HttpsRequestResolver(uri)) + { } public Task Listen(int port = DEFAULT_PORT, IPAddress ip = null) { return Listen(new IPEndPoint(ip ?? IPAddress.Any, port)); diff --git a/Examples/ClientServer/ClientServerExample.cs b/Examples/ClientServer/ClientServerExample.cs index 65840c8..0d85d94 100644 --- a/Examples/ClientServer/ClientServerExample.cs +++ b/Examples/ClientServer/ClientServerExample.cs @@ -13,7 +13,8 @@ public static void Main(string[] args) { public async static Task MainAsync() { MasterFile masterFile = new MasterFile(); - DnsServer server = new DnsServer(masterFile, "8.8.8.8"); + //Dns Proxy to Dns over https + DnsServer server = new DnsServer(masterFile, new Uri("https://1.1.1.1/dns-query")); masterFile.AddIPAddressResourceRecord("google.com", "127.0.0.1"); diff --git a/README.md b/README.md index 5a93fa6..878cd40 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ It's also possible to modify the `request` instance in the `server.Requested` ca ### Request Resolver -The `DnsServer`, `DnsClient` and `ClientRequest` classes also accept an instance implementing the `IRequestResolver` interface, which they internally use to resolve DNS requests. Some of the default implementations are `UdpRequestResolver`, `TcpRequestResolver` and `MasterFile` classes. But it's also possible to provide a custom request resolver. +The `DnsServer`, `DnsClient` and `ClientRequest` classes also accept an instance implementing the `IRequestResolver` interface, which they internally use to resolve DNS requests. Some of the default implementations are `UdpRequestResolver`, `TcpRequestResolver`, `HttpsRequestResolver` and `MasterFile` classes. But it's also possible to provide a custom request resolver. ```C# // A request resolver that resolves all dns queries to localhost @@ -124,3 +124,17 @@ DnsServer server = new DnsServer(new LocalRequestResolver()); await server.Listen(); ``` + +### Support Dns Over Https + +DNS over HTTPS (DoH), DNS queries and responses are encrypted and sent via the HTTP or HTTP/2 protocols. DoH ensures that attackers cannot forge or alter DNS traffic. DoH uses port 443, which is the standard HTTPS traffic port, to wrap the DNS query in an HTTPS request. DNS queries and responses are camouflaged within other HTTPS traffic, since it all comes and goes from the same port. + +[https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/) + +```C# +// DoH Proxy Server +MasterFile masterFile = new MasterFile(); +DnsServer server = new DnsServer(masterFile, new Uri("https://1.1.1.1/dns-query")); +// DoH Client +DnsClient client = new DnsClient(new Uri("https://1.1.1.1/dns-query")) +```