Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3.3.0

- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3.0.3
with:
dotnet-version: 2.2.x
dotnet-version: 7.0.x

- name: Test
run: |
Expand Down
3 changes: 3 additions & 0 deletions DNS/Client/DnsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
55 changes: 55 additions & 0 deletions DNS/Client/RequestResolver/HttpsRequestResolver.cs
Original file line number Diff line number Diff line change
@@ -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<IResponse> 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);
}
}
}
35 changes: 22 additions & 13 deletions DNS/Server/DnsServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {}

Expand All @@ -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));
Expand All @@ -65,7 +72,7 @@ public async Task Listen(IPEndPoint endpoint) {
udp.Client.IOControl(SIO_UDP_CONNRESET, new byte[4], new byte[4]);
}
} catch (SocketException e) {
OnError(e);
OnError(e, endpoint);
return;
}
}
Expand All @@ -76,14 +83,14 @@ void ReceiveCallback(IAsyncResult result) {
try {
IPEndPoint remote = new IPEndPoint(0, 0);
data = udp.EndReceive(result, ref remote);
HandleRequest(data, remote);
Task.Run(()=> HandleRequest(data, remote));
}
catch (ObjectDisposedException) {
// run should already be false
run = false;
}
catch (SocketException e) {
OnError(e);
OnError(e, endpoint);
}

if (run) udp.BeginReceive(ReceiveCallback, null);
Expand Down Expand Up @@ -114,8 +121,8 @@ protected virtual void Dispose(bool disposing) {
}
}

private void OnError(Exception e) {
OnEvent(Errored, new ErroredEventArgs(e));
private void OnError(Exception e, IPEndPoint remote) {
OnEvent(Errored, new ErroredEventArgs(e, remote));
}

private async void HandleRequest(byte[] data, IPEndPoint remote) {
Expand All @@ -132,12 +139,12 @@ await udp
.SendAsync(response.ToArray(), response.Size, remote)
.WithCancellationTimeout(TimeSpan.FromMilliseconds(UDP_TIMEOUT)).ConfigureAwait(false);
}
catch (SocketException e) { OnError(e); }
catch (ArgumentException e) { OnError(e); }
catch (IndexOutOfRangeException e) { OnError(e); }
catch (OperationCanceledException e) { OnError(e); }
catch (IOException e) { OnError(e); }
catch (ObjectDisposedException e) { OnError(e); }
catch (SocketException e) { OnError(e, remote); }
catch (ArgumentException e) { OnError(e, remote); }
catch (IndexOutOfRangeException e) { OnError(e, remote); }
catch (OperationCanceledException e) { OnError(e, remote); }
catch (IOException e) { OnError(e, remote); }
catch (ObjectDisposedException e) { OnError(e, remote); }
catch (ResponseException e) {
IResponse response = e.Response;

Expand All @@ -152,7 +159,7 @@ await udp
}
catch (SocketException) {}
catch (OperationCanceledException) {}
finally { OnError(e); }
finally { OnError(e, remote); }
}
}

Expand Down Expand Up @@ -183,11 +190,13 @@ public RespondedEventArgs(IRequest request, IResponse response, byte[] data, IPE
}

public class ErroredEventArgs : EventArgs {
public ErroredEventArgs(Exception e) {
public ErroredEventArgs(Exception e, IPEndPoint remote) {
Exception = e;
Remote = remote;
}

public Exception Exception { get; }
public IPEndPoint Remote { get; }
}

private class FallbackRequestResolver : IRequestResolver {
Expand Down
5 changes: 4 additions & 1 deletion DNS/Server/MasterFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ protected static bool Matches(Domain domain, Domain entry) {

for (int i = 0; i < labels.Length; i++) {
string label = labels[i];
patterns[i] = label == "*" ? "(\\w+)" : Regex.Escape(label);
// note: support hyphens '-' in domain name
// don't support underline '_' in name
patterns[i] = label == "*" ? "([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)" : Regex.Escape(label);
//patterns[i] = label == "*" ? "(\\w+)" : Regex.Escape(label);
}

Regex re = new Regex("^" + string.Join("\\.", patterns) + "$", RegexOptions.IgnoreCase);
Expand Down
3 changes: 2 additions & 1 deletion Examples/ClientServer/ClientServerExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
2 changes: 1 addition & 1 deletion Examples/Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<StartupObject />
</PropertyGroup>

Expand Down
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# DNS

A DNS library written in C# targeting .NET Standard 2.0. Versions prior to version two (2.0.0) were written for .NET 4 using blocking network operations. Version two and above use asynchronous operations.
A DNS library written in C# targeting .NET Standard 2.0.

Available through NuGet.
Only original repo is currently available through NuGet.

Install-Package DNS

[![Test](https://github.com/kapetan/dns/actions/workflows/test.yml/badge.svg)](https://github.com/kapetan/dns/actions/workflows/test.yml)
[![Test](https://github.com/jgilm/dns/actions/workflows/test.yml/badge.svg)](https://github.com/jgilm/dns/actions/workflows/test.yml)

# Usage

Expand Down Expand Up @@ -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
Expand All @@ -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"))
```
2 changes: 1 addition & 1 deletion Tests/Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down