Skip to content

Commit ca766d0

Browse files
committed
Added support for DNS over TLS and DNS over HTTPS
1 parent b6849c9 commit ca766d0

27 files changed

+1045
-299
lines changed
+52-51
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,60 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<PropertyGroup>
3-
<TargetFrameworks>net6.0</TargetFrameworks>
4-
<ImplicitUsings>enable</ImplicitUsings>
5-
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
6-
<GenerateDocumentationFile>True</GenerateDocumentationFile>
7-
<BaseOutputPath>..\bin</BaseOutputPath>
8-
<BaseIntermediateOutputPath></BaseIntermediateOutputPath>
9-
<DocumentationFile>..\bin\$(Configuration)\net6.0\ARSoft.Tools.Net.xml</DocumentationFile>
10-
<Nullable>enable</Nullable>
11-
<Title>ARSoft.Tools.Net - C#/.Net DNS client/server, SPF and SenderID Library</Title>
12-
<Authors>Alexander Reinert</Authors>
13-
<Description>This project contains a complete managed .Net DNS and DNSSEC client, a DNS server and SPF and SenderID validation.</Description>
14-
<PackageProjectUrl>https://github.com/alexreinert/ARSoft.Tools.Net</PackageProjectUrl>
15-
<PackageTags>dns dnssec spf</PackageTags>
16-
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
17-
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
18-
<Copyright>Copyright 2010..2023 Alexander Reinert</Copyright>
19-
<VersionPrefix>3.5.0</VersionPrefix>
20-
</PropertyGroup>
2+
<PropertyGroup>
3+
<TargetFrameworks>net6.0</TargetFrameworks>
4+
<ImplicitUsings>enable</ImplicitUsings>
5+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
6+
<GenerateDocumentationFile>True</GenerateDocumentationFile>
7+
<BaseOutputPath>..\bin</BaseOutputPath>
8+
<BaseIntermediateOutputPath></BaseIntermediateOutputPath>
9+
<DocumentationFile>..\bin\$(Configuration)\net6.0\ARSoft.Tools.Net.xml</DocumentationFile>
10+
<Nullable>enable</Nullable>
11+
<Title>ARSoft.Tools.Net - C#/.Net DNS client/server, SPF and SenderID Library</Title>
12+
<Authors>Alexander Reinert</Authors>
13+
<Description>This project contains a complete managed .Net DNS and DNSSEC client, a DNS server and SPF and SenderID validation.</Description>
14+
<PackageProjectUrl>https://github.com/alexreinert/ARSoft.Tools.Net</PackageProjectUrl>
15+
<PackageTags>dns dnssec spf</PackageTags>
16+
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
17+
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
18+
<Copyright>Copyright 2010..2023 Alexander Reinert</Copyright>
19+
<VersionPrefix>3.6.0</VersionPrefix>
20+
</PropertyGroup>
2121

22-
<ItemGroup>
23-
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
24-
<PackageReference Include="System.Net.Sockets" Version="4.3.0" />
25-
</ItemGroup>
22+
<ItemGroup>
23+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
24+
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
25+
<PackageReference Include="System.Net.Sockets" Version="4.3.0" />
26+
</ItemGroup>
2627

27-
<ItemGroup>
28-
<Content Include="..\LICENSE" CopyToOutputDirectory="PreserveNewest" />
29-
<Content Include="..\NOTICE" CopyToOutputDirectory="PreserveNewest" />
30-
</ItemGroup>
28+
<ItemGroup>
29+
<Content Include="..\LICENSE" CopyToOutputDirectory="PreserveNewest" />
30+
<Content Include="..\NOTICE" CopyToOutputDirectory="PreserveNewest" />
31+
</ItemGroup>
3132

32-
<ItemGroup>
33-
<None Include="..\README.md">
34-
<Pack>True</Pack>
35-
<PackagePath>\</PackagePath>
36-
</None>
37-
</ItemGroup>
33+
<ItemGroup>
34+
<None Include="..\README.md">
35+
<Pack>True</Pack>
36+
<PackagePath>\</PackagePath>
37+
</None>
38+
</ItemGroup>
3839

39-
<ItemGroup Condition="!Exists('..\..\arsoft.pfx')">
40-
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
41-
<_Parameter1>ARSoft.Tools.Net.Tests</_Parameter1>
42-
</AssemblyAttribute>
43-
</ItemGroup>
44-
<ItemGroup Condition="Exists('..\..\arsoft.pfx')">
45-
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
46-
<_Parameter1>ARSoft.Tools.Net.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001009B3C03B877D82BDB95D93615C1521BAE3C1D5E9AF140B9BE44BE07ADF2E2E303481FEF06BE780C26E9422384E9E5B0EFD7CF77B5F1F500BD79062D076F47F4F955BF3090AEEF3CE0D3FD2E9C27F496035D2055D40CFF7835CB4DC40A337C890BBE2973BDDDFEC2DE8EFB7B8B375BDBD96EE5B278D8A69866841BC5D06E817CB5</_Parameter1>
47-
</AssemblyAttribute>
48-
</ItemGroup>
40+
<ItemGroup Condition="!Exists('..\..\arsoft.pfx')">
41+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
42+
<_Parameter1>ARSoft.Tools.Net.Tests</_Parameter1>
43+
</AssemblyAttribute>
44+
</ItemGroup>
45+
<ItemGroup Condition="Exists('..\..\arsoft.pfx')">
46+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
47+
<_Parameter1>ARSoft.Tools.Net.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001009B3C03B877D82BDB95D93615C1521BAE3C1D5E9AF140B9BE44BE07ADF2E2E303481FEF06BE780C26E9422384E9E5B0EFD7CF77B5F1F500BD79062D076F47F4F955BF3090AEEF3CE0D3FD2E9C27F496035D2055D40CFF7835CB4DC40A337C890BBE2973BDDDFEC2DE8EFB7B8B375BDBD96EE5B278D8A69866841BC5D06E817CB5</_Parameter1>
48+
</AssemblyAttribute>
49+
</ItemGroup>
4950

50-
<PropertyGroup Condition="Exists('..\..\arsoft.pfx')">
51-
<SignAssembly>true</SignAssembly>
52-
<AssemblyOriginatorKeyFile>..\..\arsoft.pfx</AssemblyOriginatorKeyFile>
53-
</PropertyGroup>
51+
<PropertyGroup Condition="Exists('..\..\arsoft.pfx')">
52+
<SignAssembly>true</SignAssembly>
53+
<AssemblyOriginatorKeyFile>..\..\arsoft.pfx</AssemblyOriginatorKeyFile>
54+
</PropertyGroup>
5455

55-
<PropertyGroup>
56-
<NoWarn>1701;1702;1591</NoWarn>
57-
<PackageReadmeFile>README.md</PackageReadmeFile>
58-
</PropertyGroup>
56+
<PropertyGroup>
57+
<NoWarn>1701;1702;1591</NoWarn>
58+
<PackageReadmeFile>README.md</PackageReadmeFile>
59+
</PropertyGroup>
5960
</Project>

ARSoft.Tools.Net/BaseEncoding.cs

+11-6
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ public static byte[] FromBase64String(this string inData)
415415
/// <returns> Decoded data </returns>
416416
public static byte[] FromBase64CharArray(this char[] inData, int offset, int length)
417417
{
418-
return inData.FromBase64CharArray(offset, length, _base64ReverseAlphabet);
418+
return inData.FromBase64CharArray(0, offset, length, _base64ReverseAlphabet);
419419
}
420420

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

459+
internal static byte[] FromBase64UrlString(this string inData, int prefixBytes)
460+
{
461+
return inData.ToCharArray().FromBase64CharArray(prefixBytes, 0, inData.Length, _base64UrlReverseAlphabet);
462+
}
463+
459464
/// <summary>
460465
/// Decodes a Base64Url char array as described in <a href="https://www.rfc-editor.org/rfc/rfc4648.html">RFC 4648</a>.
461466
/// </summary>
@@ -465,7 +470,7 @@ public static byte[] FromBase64UrlString(this string inData)
465470
/// <returns> Decoded data </returns>
466471
public static byte[] FromBase64UrlCharArray(this char[] inData, int offset, int length)
467472
{
468-
return inData.FromBase64CharArray(offset, length, _base64UrlReverseAlphabet);
473+
return inData.FromBase64CharArray(0, offset, length, _base64UrlReverseAlphabet);
469474
}
470475

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

495-
private static byte[] FromBase64CharArray(this char[] inData, int offset, int length, Dictionary<char, byte> alphabet)
500+
private static byte[] FromBase64CharArray(this char[] inData, int prefixBytes, int offset, int length, Dictionary<char, byte> alphabet)
496501
{
497502
int paddingCount;
498503
int remain;
499504

500505
if (length == 0)
501-
return Array.Empty<byte>();
506+
return new byte[prefixBytes];
502507

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

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

521-
byte[] res = new byte[outSafeLength + remain];
526+
byte[] res = new byte[prefixBytes + outSafeLength + remain];
522527

523528
int inPos = offset;
524-
int outPos = 0;
529+
int outPos = prefixBytes;
525530

526531
byte[] buffer = new byte[4];
527532

ARSoft.Tools.Net/Dns/DnsRecord/ZoneMDRecord.cs

+28-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
1-
using Org.BouncyCastle.Crypto.Digests;
1+
#region Copyright and License
2+
// Copyright 2010..2023 Alexander Reinert
3+
//
4+
// This file is part of the ARSoft.Tools.Net - C# DNS client/server and SPF Library (https://github.com/alexreinert/ARSoft.Tools.Net)
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
#endregion
18+
19+
using Org.BouncyCastle.Crypto.Digests;
220
using Org.BouncyCastle.Crypto;
321
using System;
422
using System.Collections.Generic;
@@ -85,8 +103,8 @@ internal ZoneMDRecord(DomainName name, RecordType recordType, RecordClass record
85103
: base(name, recordType, recordClass, timeToLive)
86104
{
87105
SerialNumber = DnsMessageBase.ParseUInt(resultData, ref currentPosition);
88-
Scheme = (ZoneMDScheme)resultData[currentPosition++];
89-
HashAlgorithm = (ZoneMDHashAlgorithm)resultData[currentPosition++];
106+
Scheme = (ZoneMDScheme) resultData[currentPosition++];
107+
HashAlgorithm = (ZoneMDHashAlgorithm) resultData[currentPosition++];
90108
Digest = DnsMessageBase.ParseByteData(resultData, ref currentPosition, length - 6);
91109
}
92110

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

99117
SerialNumber = UInt32.Parse(stringRepresentation[0]);
100-
Scheme = (ZoneMDScheme)Byte.Parse(stringRepresentation[1]);
101-
HashAlgorithm = (ZoneMDHashAlgorithm)Byte.Parse(stringRepresentation[2]);
118+
Scheme = (ZoneMDScheme) Byte.Parse(stringRepresentation[1]);
119+
HashAlgorithm = (ZoneMDHashAlgorithm) Byte.Parse(stringRepresentation[2]);
102120
Digest = String.Join(String.Empty, stringRepresentation.Skip(3)).FromBase16String();
103121
}
104122

@@ -123,8 +141,8 @@ public ZoneMDRecord(DomainName name, int timeToLive, uint serialNumber, ZoneMDSc
123141
internal override string RecordDataToString()
124142
{
125143
return SerialNumber
126-
+ " " + (byte)Scheme
127-
+ " " + (byte)HashAlgorithm
144+
+ " " + (byte) Scheme
145+
+ " " + (byte) HashAlgorithm
128146
+ " " + Digest.ToBase16String();
129147
}
130148

@@ -133,8 +151,8 @@ internal override string RecordDataToString()
133151
protected internal override void EncodeRecordData(IList<byte> messageData, ref int currentPosition, Dictionary<DomainName, ushort>? domainNames, bool useCanonical)
134152
{
135153
DnsMessageBase.EncodeUInt(messageData, ref currentPosition, SerialNumber);
136-
messageData[currentPosition++] = (byte)Scheme;
137-
messageData[currentPosition++] = (byte)HashAlgorithm;
154+
messageData[currentPosition++] = (byte) Scheme;
155+
messageData[currentPosition++] = (byte) HashAlgorithm;
138156
DnsMessageBase.EncodeByteArray(messageData, ref currentPosition, Digest);
139157
}
140158

@@ -184,7 +202,7 @@ private byte[] CalculateZoneMDDigest(Zone zone)
184202
continue;
185203

186204
// ignore RRSIG records covering ZONEMD records at zone apex
187-
if (record.Name.Equals(Name) && record.RecordType == RecordType.RrSig && ((RrSigRecord)record).TypeCovered == RecordType.ZoneMD)
205+
if (record.Name.Equals(Name) && record.RecordType == RecordType.RrSig && ((RrSigRecord) record).TypeCovered == RecordType.ZoneMD)
188206
continue;
189207

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

203221
return hash;
204222
}
205-
206223
}
207224

208225
internal static class ZoneMDHelper
@@ -231,5 +248,4 @@ public static bool IsSupported(this ZoneMDRecord.ZoneMDScheme scheme)
231248
return false;
232249
}
233250
}
234-
235251
}

ARSoft.Tools.Net/Dns/DnsSec/KeyRecordBase.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,8 @@ protected KeyRecordBase(DomainName name, RecordType recordType, RecordClass reco
336336
throw new FormatException();
337337

338338
Flags = UInt16.Parse(stringRepresentation[0]);
339-
Protocol = (ProtocolType)Byte.Parse(stringRepresentation[1]);
340-
Algorithm = (DnsSecAlgorithm)Byte.Parse(stringRepresentation[2]);
339+
Protocol = (ProtocolType) Byte.Parse(stringRepresentation[1]);
340+
Algorithm = (DnsSecAlgorithm) Byte.Parse(stringRepresentation[2]);
341341
}
342342

343343
internal sealed override string RecordDataToString()

ARSoft.Tools.Net/Dns/DnsSec/NSec3ParamRecord.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public NSec3ParamRecord(DomainName name, RecordClass recordClass, int timeToLive
9696
internal override string RecordDataToString()
9797
{
9898
return (byte) HashAlgorithm
99-
+ " " + (byte)Flags
99+
+ " " + (byte) Flags
100100
+ " " + Iterations
101101
+ " " + ((Salt.Length == 0) ? "-" : Salt.ToBase16String());
102102
}

ARSoft.Tools.Net/Dns/DnsSec/Nsec3Record.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public NSec3Record(DomainName name, RecordClass recordClass, int timeToLive, NSe
127127
internal override string RecordDataToString()
128128
{
129129
return (byte) HashAlgorithm
130-
+ " " + (byte)Flags
130+
+ " " + (byte) Flags
131131
+ " " + Iterations
132132
+ " " + ((Salt.Length == 0) ? "-" : Salt.ToBase16String())
133133
+ " " + NextHashedOwner.ToBase32String()

ARSoft.Tools.Net/Dns/DnsServer.cs

+41-12
Original file line numberDiff line numberDiff line change
@@ -121,39 +121,64 @@ private async void ConnectionLoopAsync(IServerTransport transport, CancellationT
121121
}
122122
}
123123

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

129-
if (clientConnectedEventArgs.RefuseConnect)
130-
return;
129+
public RefCountDispose(IDisposable disposable)
130+
{
131+
_disposable = disposable;
132+
}
133+
134+
public void Increment()
135+
{
136+
Interlocked.Increment(ref _count);
137+
}
138+
139+
public void Decrement()
140+
{
141+
if (Interlocked.Decrement(ref _count) <= 0)
142+
_disposable.TryDispose();
143+
}
144+
}
145+
146+
private async void ProcessConnectionAsync(IServerConnection connection, CancellationToken token)
147+
{
148+
var refCount = new RefCountDispose(connection);
131149

132150
try
133151
{
152+
var clientConnectedEventArgs = new ClientConnectedEventArgs(connection.Transport.TransportProtocol, connection.RemoteEndPoint, connection.LocalEndPoint);
153+
await ClientConnected.RaiseAsync(this, clientConnectedEventArgs);
154+
155+
if (clientConnectedEventArgs.RefuseConnect)
156+
return;
157+
158+
if (!await connection.InitializeAsync(token))
159+
return;
160+
134161
while (connection.CanRead)
135162
{
163+
refCount.Increment();
136164
var queryPackage = await connection.ReceiveAsync(token);
137165

138166
if (queryPackage == null)
139167
break;
140168

141169
#pragma warning disable CS4014
142-
Task.Run(() => ProcessRawPackageAsync(connection, queryPackage, token), token);
170+
Task.Run(() => ProcessRawPackageAsync(connection, queryPackage, refCount, token), token);
143171
#pragma warning restore CS4014
144172
}
145173
}
146174
catch (Exception ex)
147175
{
148176
OnExceptionThrownAsync(ex);
149-
}
150-
finally
151-
{
152-
connection.TryDispose();
177+
refCount.Decrement();
153178
}
154179
}
155180

156-
private async Task ProcessRawPackageAsync(IServerConnection connection, DnsReceivedRawPackage queryPackage, CancellationToken token)
181+
private async Task ProcessRawPackageAsync(IServerConnection connection, DnsReceivedRawPackage queryPackage, RefCountDispose refCount, CancellationToken token)
157182
{
158183
try
159184
{
@@ -189,7 +214,7 @@ private async Task ProcessRawPackageAsync(IServerConnection connection, DnsRecei
189214
}
190215
else
191216
{
192-
if (response.AllowMultipleResponses)
217+
if (response.AllowMultipleResponses && connection.Transport.SupportsMultipleResponses)
193218
{
194219
var isSubSequentResponse = false;
195220

@@ -310,6 +335,10 @@ private async Task ProcessRawPackageAsync(IServerConnection connection, DnsRecei
310335
{
311336
OnExceptionThrownAsync(ex);
312337
}
338+
finally
339+
{
340+
refCount.Decrement();
341+
}
313342
}
314343

315344
private async Task<DnsMessageBase> ProcessMessageAsync(DnsMessageBase query, TransportProtocol transportProtocol, IPEndPoint remoteEndpoint)

0 commit comments

Comments
 (0)