Skip to content

Commit

Permalink
Added support for DNS over TLS and DNS over HTTPS
Browse files Browse the repository at this point in the history
  • Loading branch information
alexreinert committed Nov 26, 2023
1 parent b6849c9 commit ca766d0
Show file tree
Hide file tree
Showing 27 changed files with 1,045 additions and 299 deletions.
103 changes: 52 additions & 51 deletions ARSoft.Tools.Net/ARSoft.Tools.Net.csproj
Original file line number Diff line number Diff line change
@@ -1,59 +1,60 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<BaseOutputPath>..\bin</BaseOutputPath>
<BaseIntermediateOutputPath></BaseIntermediateOutputPath>
<DocumentationFile>..\bin\$(Configuration)\net6.0\ARSoft.Tools.Net.xml</DocumentationFile>
<Nullable>enable</Nullable>
<Title>ARSoft.Tools.Net - C#/.Net DNS client/server, SPF and SenderID Library</Title>
<Authors>Alexander Reinert</Authors>
<Description>This project contains a complete managed .Net DNS and DNSSEC client, a DNS server and SPF and SenderID validation.</Description>
<PackageProjectUrl>https://github.com/alexreinert/ARSoft.Tools.Net</PackageProjectUrl>
<PackageTags>dns dnssec spf</PackageTags>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Copyright>Copyright 2010..2023 Alexander Reinert</Copyright>
<VersionPrefix>3.5.0</VersionPrefix>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<BaseOutputPath>..\bin</BaseOutputPath>
<BaseIntermediateOutputPath></BaseIntermediateOutputPath>
<DocumentationFile>..\bin\$(Configuration)\net6.0\ARSoft.Tools.Net.xml</DocumentationFile>
<Nullable>enable</Nullable>
<Title>ARSoft.Tools.Net - C#/.Net DNS client/server, SPF and SenderID Library</Title>
<Authors>Alexander Reinert</Authors>
<Description>This project contains a complete managed .Net DNS and DNSSEC client, a DNS server and SPF and SenderID validation.</Description>
<PackageProjectUrl>https://github.com/alexreinert/ARSoft.Tools.Net</PackageProjectUrl>
<PackageTags>dns dnssec spf</PackageTags>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Copyright>Copyright 2010..2023 Alexander Reinert</Copyright>
<VersionPrefix>3.6.0</VersionPrefix>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
<PackageReference Include="System.Net.Sockets" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
<PackageReference Include="System.Net.Sockets" Version="4.3.0" />
</ItemGroup>

<ItemGroup>
<Content Include="..\LICENSE" CopyToOutputDirectory="PreserveNewest" />
<Content Include="..\NOTICE" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<Content Include="..\LICENSE" CopyToOutputDirectory="PreserveNewest" />
<Content Include="..\NOTICE" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup Condition="!Exists('..\..\arsoft.pfx')">
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>ARSoft.Tools.Net.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup Condition="Exists('..\..\arsoft.pfx')">
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>ARSoft.Tools.Net.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001009B3C03B877D82BDB95D93615C1521BAE3C1D5E9AF140B9BE44BE07ADF2E2E303481FEF06BE780C26E9422384E9E5B0EFD7CF77B5F1F500BD79062D076F47F4F955BF3090AEEF3CE0D3FD2E9C27F496035D2055D40CFF7835CB4DC40A337C890BBE2973BDDDFEC2DE8EFB7B8B375BDBD96EE5B278D8A69866841BC5D06E817CB5</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup Condition="!Exists('..\..\arsoft.pfx')">
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>ARSoft.Tools.Net.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup Condition="Exists('..\..\arsoft.pfx')">
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>ARSoft.Tools.Net.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001009B3C03B877D82BDB95D93615C1521BAE3C1D5E9AF140B9BE44BE07ADF2E2E303481FEF06BE780C26E9422384E9E5B0EFD7CF77B5F1F500BD79062D076F47F4F955BF3090AEEF3CE0D3FD2E9C27F496035D2055D40CFF7835CB4DC40A337C890BBE2973BDDDFEC2DE8EFB7B8B375BDBD96EE5B278D8A69866841BC5D06E817CB5</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

<PropertyGroup Condition="Exists('..\..\arsoft.pfx')">
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\arsoft.pfx</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition="Exists('..\..\arsoft.pfx')">
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\arsoft.pfx</AssemblyOriginatorKeyFile>
</PropertyGroup>

<PropertyGroup>
<NoWarn>1701;1702;1591</NoWarn>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup>
<NoWarn>1701;1702;1591</NoWarn>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
</Project>
17 changes: 11 additions & 6 deletions ARSoft.Tools.Net/BaseEncoding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ public static byte[] FromBase64String(this string inData)
/// <returns> Decoded data </returns>
public static byte[] FromBase64CharArray(this char[] inData, int offset, int length)
{
return inData.FromBase64CharArray(offset, length, _base64ReverseAlphabet);
return inData.FromBase64CharArray(0, offset, length, _base64ReverseAlphabet);
}

/// <summary>
Expand Down Expand Up @@ -456,6 +456,11 @@ public static byte[] FromBase64UrlString(this string inData)
return inData.ToCharArray().FromBase64UrlCharArray(0, inData.Length);
}

internal static byte[] FromBase64UrlString(this string inData, int prefixBytes)
{
return inData.ToCharArray().FromBase64CharArray(prefixBytes, 0, inData.Length, _base64UrlReverseAlphabet);
}

/// <summary>
/// Decodes a Base64Url char array as described in <a href="https://www.rfc-editor.org/rfc/rfc4648.html">RFC 4648</a>.
/// </summary>
Expand All @@ -465,7 +470,7 @@ public static byte[] FromBase64UrlString(this string inData)
/// <returns> Decoded data </returns>
public static byte[] FromBase64UrlCharArray(this char[] inData, int offset, int length)
{
return inData.FromBase64CharArray(offset, length, _base64UrlReverseAlphabet);
return inData.FromBase64CharArray(0, offset, length, _base64UrlReverseAlphabet);
}

/// <summary>
Expand All @@ -492,13 +497,13 @@ public static string ToBase64UrlString(this byte[] inArray, int offset, int leng
return inArray.ToBase64String(offset, length, _base64UrlAlphabet);
}

private static byte[] FromBase64CharArray(this char[] inData, int offset, int length, Dictionary<char, byte> alphabet)
private static byte[] FromBase64CharArray(this char[] inData, int prefixBytes, int offset, int length, Dictionary<char, byte> alphabet)
{
int paddingCount;
int remain;

if (length == 0)
return Array.Empty<byte>();
return new byte[prefixBytes];

if (alphabet[inData[offset + length - 2]] == 64)
{
Expand All @@ -518,10 +523,10 @@ private static byte[] FromBase64CharArray(this char[] inData, int offset, int le

int outSafeLength = (length - paddingCount) / 4 * 3;

byte[] res = new byte[outSafeLength + remain];
byte[] res = new byte[prefixBytes + outSafeLength + remain];

int inPos = offset;
int outPos = 0;
int outPos = prefixBytes;

byte[] buffer = new byte[4];

Expand Down
40 changes: 28 additions & 12 deletions ARSoft.Tools.Net/Dns/DnsRecord/ZoneMDRecord.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
using Org.BouncyCastle.Crypto.Digests;
#region Copyright and License
// Copyright 2010..2023 Alexander Reinert
//
// This file is part of the ARSoft.Tools.Net - C# DNS client/server and SPF Library (https://github.com/alexreinert/ARSoft.Tools.Net)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion

using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -85,8 +103,8 @@ internal ZoneMDRecord(DomainName name, RecordType recordType, RecordClass record
: base(name, recordType, recordClass, timeToLive)
{
SerialNumber = DnsMessageBase.ParseUInt(resultData, ref currentPosition);
Scheme = (ZoneMDScheme)resultData[currentPosition++];
HashAlgorithm = (ZoneMDHashAlgorithm)resultData[currentPosition++];
Scheme = (ZoneMDScheme) resultData[currentPosition++];
HashAlgorithm = (ZoneMDHashAlgorithm) resultData[currentPosition++];
Digest = DnsMessageBase.ParseByteData(resultData, ref currentPosition, length - 6);
}

Expand All @@ -97,8 +115,8 @@ internal ZoneMDRecord(DomainName name, RecordType recordType, RecordClass record
throw new FormatException();

SerialNumber = UInt32.Parse(stringRepresentation[0]);
Scheme = (ZoneMDScheme)Byte.Parse(stringRepresentation[1]);
HashAlgorithm = (ZoneMDHashAlgorithm)Byte.Parse(stringRepresentation[2]);
Scheme = (ZoneMDScheme) Byte.Parse(stringRepresentation[1]);
HashAlgorithm = (ZoneMDHashAlgorithm) Byte.Parse(stringRepresentation[2]);
Digest = String.Join(String.Empty, stringRepresentation.Skip(3)).FromBase16String();
}

Expand All @@ -123,8 +141,8 @@ public ZoneMDRecord(DomainName name, int timeToLive, uint serialNumber, ZoneMDSc
internal override string RecordDataToString()
{
return SerialNumber
+ " " + (byte)Scheme
+ " " + (byte)HashAlgorithm
+ " " + (byte) Scheme
+ " " + (byte) HashAlgorithm
+ " " + Digest.ToBase16String();
}

Expand All @@ -133,8 +151,8 @@ internal override string RecordDataToString()
protected internal override void EncodeRecordData(IList<byte> messageData, ref int currentPosition, Dictionary<DomainName, ushort>? domainNames, bool useCanonical)
{
DnsMessageBase.EncodeUInt(messageData, ref currentPosition, SerialNumber);
messageData[currentPosition++] = (byte)Scheme;
messageData[currentPosition++] = (byte)HashAlgorithm;
messageData[currentPosition++] = (byte) Scheme;
messageData[currentPosition++] = (byte) HashAlgorithm;
DnsMessageBase.EncodeByteArray(messageData, ref currentPosition, Digest);
}

Expand Down Expand Up @@ -184,7 +202,7 @@ private byte[] CalculateZoneMDDigest(Zone zone)
continue;

// ignore RRSIG records covering ZONEMD records at zone apex
if (record.Name.Equals(Name) && record.RecordType == RecordType.RrSig && ((RrSigRecord)record).TypeCovered == RecordType.ZoneMD)
if (record.Name.Equals(Name) && record.RecordType == RecordType.RrSig && ((RrSigRecord) record).TypeCovered == RecordType.ZoneMD)
continue;

lastRecord = record;
Expand All @@ -202,7 +220,6 @@ private byte[] CalculateZoneMDDigest(Zone zone)

return hash;
}

}

internal static class ZoneMDHelper
Expand Down Expand Up @@ -231,5 +248,4 @@ public static bool IsSupported(this ZoneMDRecord.ZoneMDScheme scheme)
return false;
}
}

}
4 changes: 2 additions & 2 deletions ARSoft.Tools.Net/Dns/DnsSec/KeyRecordBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@ protected KeyRecordBase(DomainName name, RecordType recordType, RecordClass reco
throw new FormatException();

Flags = UInt16.Parse(stringRepresentation[0]);
Protocol = (ProtocolType)Byte.Parse(stringRepresentation[1]);
Algorithm = (DnsSecAlgorithm)Byte.Parse(stringRepresentation[2]);
Protocol = (ProtocolType) Byte.Parse(stringRepresentation[1]);
Algorithm = (DnsSecAlgorithm) Byte.Parse(stringRepresentation[2]);
}

internal sealed override string RecordDataToString()
Expand Down
2 changes: 1 addition & 1 deletion ARSoft.Tools.Net/Dns/DnsSec/NSec3ParamRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public NSec3ParamRecord(DomainName name, RecordClass recordClass, int timeToLive
internal override string RecordDataToString()
{
return (byte) HashAlgorithm
+ " " + (byte)Flags
+ " " + (byte) Flags
+ " " + Iterations
+ " " + ((Salt.Length == 0) ? "-" : Salt.ToBase16String());
}
Expand Down
2 changes: 1 addition & 1 deletion ARSoft.Tools.Net/Dns/DnsSec/Nsec3Record.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public NSec3Record(DomainName name, RecordClass recordClass, int timeToLive, NSe
internal override string RecordDataToString()
{
return (byte) HashAlgorithm
+ " " + (byte)Flags
+ " " + (byte) Flags
+ " " + Iterations
+ " " + ((Salt.Length == 0) ? "-" : Salt.ToBase16String())
+ " " + NextHashedOwner.ToBase32String()
Expand Down
53 changes: 41 additions & 12 deletions ARSoft.Tools.Net/Dns/DnsServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,39 +121,64 @@ private async void ConnectionLoopAsync(IServerTransport transport, CancellationT
}
}

private async void ProcessConnectionAsync(IServerConnection connection, CancellationToken token)
private class RefCountDispose
{
var clientConnectedEventArgs = new ClientConnectedEventArgs(connection.Transport.TransportProtocol, connection.RemoteEndPoint, connection.LocalEndPoint);
await ClientConnected.RaiseAsync(this, clientConnectedEventArgs);
private int _count = 0;
private readonly IDisposable _disposable;

if (clientConnectedEventArgs.RefuseConnect)
return;
public RefCountDispose(IDisposable disposable)
{
_disposable = disposable;
}

public void Increment()
{
Interlocked.Increment(ref _count);
}

public void Decrement()
{
if (Interlocked.Decrement(ref _count) <= 0)
_disposable.TryDispose();
}
}

private async void ProcessConnectionAsync(IServerConnection connection, CancellationToken token)
{
var refCount = new RefCountDispose(connection);

try
{
var clientConnectedEventArgs = new ClientConnectedEventArgs(connection.Transport.TransportProtocol, connection.RemoteEndPoint, connection.LocalEndPoint);
await ClientConnected.RaiseAsync(this, clientConnectedEventArgs);

if (clientConnectedEventArgs.RefuseConnect)
return;

if (!await connection.InitializeAsync(token))
return;

while (connection.CanRead)
{
refCount.Increment();
var queryPackage = await connection.ReceiveAsync(token);

if (queryPackage == null)
break;

#pragma warning disable CS4014
Task.Run(() => ProcessRawPackageAsync(connection, queryPackage, token), token);
Task.Run(() => ProcessRawPackageAsync(connection, queryPackage, refCount, token), token);
#pragma warning restore CS4014
}
}
catch (Exception ex)
{
OnExceptionThrownAsync(ex);
}
finally
{
connection.TryDispose();
refCount.Decrement();
}
}

private async Task ProcessRawPackageAsync(IServerConnection connection, DnsReceivedRawPackage queryPackage, CancellationToken token)
private async Task ProcessRawPackageAsync(IServerConnection connection, DnsReceivedRawPackage queryPackage, RefCountDispose refCount, CancellationToken token)
{
try
{
Expand Down Expand Up @@ -189,7 +214,7 @@ private async Task ProcessRawPackageAsync(IServerConnection connection, DnsRecei
}
else
{
if (response.AllowMultipleResponses)
if (response.AllowMultipleResponses && connection.Transport.SupportsMultipleResponses)
{
var isSubSequentResponse = false;

Expand Down Expand Up @@ -310,6 +335,10 @@ private async Task ProcessRawPackageAsync(IServerConnection connection, DnsRecei
{
OnExceptionThrownAsync(ex);
}
finally
{
refCount.Decrement();
}
}

private async Task<DnsMessageBase> ProcessMessageAsync(DnsMessageBase query, TransportProtocol transportProtocol, IPEndPoint remoteEndpoint)
Expand Down
Loading

0 comments on commit ca766d0

Please sign in to comment.