Skip to content

Commit

Permalink
Adding .NET 6 target and minor improvements (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaCo authored Oct 23, 2022
1 parent 53b94f1 commit 4a8266b
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 84 deletions.
3 changes: 1 addition & 2 deletions samples/MiniDig/MiniDig.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>MiniDig</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>MiniDig</PackageId>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
Expand Down
10 changes: 6 additions & 4 deletions src/DnsClient/DnsClient.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>1.6.1</VersionPrefix>
<VersionPrefix>1.7.0</VersionPrefix>
<VersionSuffix Condition="'$(VersionSuffix)'!='' AND '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>

<TargetFrameworks>net5.0;netstandard1.3;netstandard2.0;netstandard2.1;net45;net471</TargetFrameworks>
<TargetFrameworks>net6.0;net5.0;netstandard1.3;netstandard2.0;netstandard2.1;net45;net471</TargetFrameworks>
<DebugType>full</DebugType>

<Product>DnsClient.NET</Product>
Expand All @@ -21,11 +21,12 @@
<PackageTags>dns;client;stub;resolver;name;server;core;service;discovery</PackageTags>

<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageProjectUrl>http://dnsclient.michaco.net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<WebPage>http://dnsclient.michaco.net</WebPage>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/MichaCo/DnsClient.NET</RepositoryUrl>
<RepositoryUrl>https://github.com/MichaCo/DnsClient.NET</RepositoryUrl>

<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>latest</LangVersion>
Expand All @@ -40,6 +41,7 @@

<ItemGroup>
<None Include="..\..\icon.png" Pack="true" PackagePath="" />
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' != 'net45' ">
Expand Down
48 changes: 33 additions & 15 deletions src/DnsClient/DnsDatagramReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@

namespace DnsClient
{
internal class DnsDatagramReader
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS3002 // Return type is not CLS-compliant

/// <summary>
/// Helper to read from DNS datagrams.
/// </summary>
/// <remarks>
/// The API of this class might change over time and receive breaking changes. Use at own risk.
/// </remarks>
public class DnsDatagramReader
{
public const int IPv6Length = 16;
public const int IPv4Length = 4;
private const byte ReferenceByte = 0xc0;
private const string ACEPrefix = "xn--";
private const int MaxRecursion = 100;

private readonly byte[] _ipV4Buffer = new byte[4];
private readonly byte[] _ipV6Buffer = new byte[16];
Expand Down Expand Up @@ -181,18 +191,6 @@ public void Advance(int length)
Index += length;
}

public void SanitizeResult(int expectedIndex, int dataLength)
{
if (Index != expectedIndex)
{
throw new DnsResponseParseException(
message: $"Record reader index out of sync. Expected to read till {expectedIndex} but tried to read till index {Index}.",
data: _data.ToArray(),
index: Index,
length: dataLength);
}
}

public DnsString ReadDnsName()
{
var builder = StringBuilderObjectPool.Default.Get();
Expand Down Expand Up @@ -275,8 +273,13 @@ public DnsString ReadQuestionQueryString()
return DnsString.FromResponseQueryString(value);
}

public ICollection<ArraySegment<byte>> ReadLabels()
public IReadOnlyList<ArraySegment<byte>> ReadLabels(int recursion = 0)
{
if (recursion++ >= MaxRecursion)
{
throw new DnsResponseParseException("Max recursion reached.", _data.ToArray(), Index, 0);
}

var result = new List<ArraySegment<byte>>(10);

// read the length byte for the label, then get the content from offset+1 to length
Expand All @@ -300,7 +303,7 @@ public ICollection<ArraySegment<byte>> ReadLabels()
}

var subReader = new DnsDatagramReader(_data.SubArrayFromOriginal(subIndex));
var newLabels = subReader.ReadLabels();
var newLabels = subReader.ReadLabels(recursion);
result.AddRange(newLabels); // add range actually much faster than concat and equal to or faster than for-each.. (array copy would work maybe)
return result;
}
Expand Down Expand Up @@ -353,6 +356,18 @@ public uint ReadUInt32NetworkOrder()

return (uint)(ReadUInt16NetworkOrder() << 16 | ReadUInt16NetworkOrder());
}

internal void SanitizeResult(int expectedIndex, int dataLength)
{
if (Index != expectedIndex)
{
throw new DnsResponseParseException(
message: $"Record reader index out of sync. Expected to read till {expectedIndex} but tried to read till index {Index}.",
data: _data.ToArray(),
index: Index,
length: dataLength);
}
}
}

internal static class ArraySegmentExtensions
Expand All @@ -367,4 +382,7 @@ public static ArraySegment<T> SubArrayFromOriginal<T>(this ArraySegment<T> array
return new ArraySegment<T>(array.Array, startIndex, array.Array.Length - startIndex);
}
}

#pragma warning restore CS3002 // Return type is not CLS-compliant
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}
10 changes: 9 additions & 1 deletion src/DnsClient/DnsUdpMessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,15 @@ public override async Task<DnsResponseMessage> QueryAsync(

using (var memory = new PooledBytes(readSize))
{
#if !NET45

#if NET6_0_OR_GREATER
int received = await udpClient.Client.ReceiveAsync(
new ArraySegment<byte>(memory.Buffer),
SocketFlags.None,
cancellationToken: cancellationToken).ConfigureAwait(false);

var response = GetResponseMessage(new ArraySegment<byte>(memory.Buffer, 0, received));
#elif !NET45
int received = await udpClient.Client.ReceiveAsync(new ArraySegment<byte>(memory.Buffer), SocketFlags.None).ConfigureAwait(false);

var response = GetResponseMessage(new ArraySegment<byte>(memory.Buffer, 0, received));
Expand Down
2 changes: 1 addition & 1 deletion src/DnsClient/Internal/PooledBytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private void Dispose(bool disposing)
if (disposing && !_disposed)
{
_disposed = true;
s_pool.Return(_buffer.Array);
s_pool.Return(_buffer.Array, clearArray: true);
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions src/DnsClient/Interop/Windows/NameResolutionPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

namespace DnsClient.Windows
{
#if !NET45

internal static class NameResolutionPolicy
{
private static readonly char[] s_splitOn = new char[] { ';' };
Expand All @@ -19,12 +21,16 @@ internal static class NameResolutionPolicy
internal static IReadOnlyCollection<NameServer> Resolve(bool includeGenericServers = true, bool includeDirectAccessServers = true)
{
var nameServers = new HashSet<NameServer>();
#if !NET45

#if NET5_0_OR_GREATER
if (!OperatingSystem.IsWindows())
#else
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
#endif
{
return nameServers;
}
#endif

// [MS-GPNRPT] dictates that the NRPT is stored in two separate registry keys.
//
// - The Policy key is pushed down through Group Policy.
Expand Down Expand Up @@ -123,4 +129,5 @@ private static void AddServers(HashSet<NameServer> nameServers, string[] names,
}
}
}
#endif
}
96 changes: 59 additions & 37 deletions src/DnsClient/NameServer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
Expand Down Expand Up @@ -254,7 +255,7 @@ public static IReadOnlyCollection<NameServer> ResolveNameServers(bool skipIPv6Si
}
catch (Exception ex)
{
logger?.LogInformation(ex, "Resolving name servers using .NET framework failed.");
logger?.LogWarning(ex, "Resolving name servers using .NET framework failed.");
exceptions.Add(ex);
}

Expand All @@ -270,7 +271,7 @@ public static IReadOnlyCollection<NameServer> ResolveNameServers(bool skipIPv6Si
}
catch (Exception ex)
{
logger?.LogInformation(ex, "Resolving name servers using native implementation failed.");
logger?.LogWarning(ex, "Resolving name servers using native implementation failed.");
exceptions.Add(ex);
}
}
Expand Down Expand Up @@ -305,36 +306,39 @@ public static IReadOnlyCollection<NameServer> ResolveNameServers(bool skipIPv6Si
}

#endif
if (!fallbackToGooglePublicDns && exceptions.Count > 0)
{
if (exceptions.Count > 1)
{
throw new AggregateException("Error resolving name servers", exceptions);
}
else
{
throw new InvalidOperationException("Error resolving name servers", exceptions.First());
}
}

IReadOnlyCollection<NameServer> filtered = nameServers
.Where(p => (p.IPEndPoint.Address.AddressFamily == AddressFamily.InterNetwork
|| p.IPEndPoint.Address.AddressFamily == AddressFamily.InterNetworkV6)
&& (!p.IPEndPoint.Address.IsIPv6SiteLocal || !skipIPv6SiteLocal))
.ToArray();

filtered = ValidateNameServers(filtered, logger);
try
{
filtered = ValidateNameServers(filtered, logger);
}
catch (Exception ex)
{
logger?.LogWarning(ex, "NameServer validation failed.");
exceptions.Add(ex);
}

if (filtered.Count == 0 && fallbackToGooglePublicDns)
if (filtered.Count == 0)
{
logger?.LogWarning("Could not resolve any NameServers, falling back to Google public servers.");
return new NameServer[]
if (!fallbackToGooglePublicDns && exceptions.Count > 0)
{
GooglePublicDnsIPv6,
GooglePublicDns2IPv6,
GooglePublicDns,
GooglePublicDns2,
};
throw new InvalidOperationException("Could not resolve any NameServers.", exceptions.First());
}
else if (fallbackToGooglePublicDns)
{
logger?.LogWarning("Could not resolve any NameServers, falling back to Google public servers.");
return new NameServer[]
{
GooglePublicDns,
GooglePublicDns2,
GooglePublicDnsIPv6,
GooglePublicDns2IPv6
};
}
}

logger?.LogDebug("Resolved {0} name servers: [{1}].", filtered.Count, string.Join(",", filtered.AsEnumerable()));
Expand All @@ -357,18 +361,37 @@ public static IReadOnlyCollection<NameServer> ResolveNameServers(bool skipIPv6Si
public static IReadOnlyCollection<NameServer> ResolveNameServersNative()
{
List<NameServer> addresses = new List<NameServer>();

#if NET5_0_OR_GREATER
if (OperatingSystem.IsWindows())
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
#endif
{
var fixedInfo = Windows.IpHlpApi.FixedNetworkInformation.GetFixedInformation();

foreach (var ip in fixedInfo.DnsAddresses)
try
{
addresses.Add(new NameServer(ip, DefaultPort, fixedInfo.DomainName));
var fixedInfo = Windows.IpHlpApi.FixedNetworkInformation.GetFixedInformation();

foreach (var ip in fixedInfo.DnsAddresses)
{
addresses.Add(new NameServer(ip, DefaultPort, fixedInfo.DomainName));
}
}
catch { }
}
#if NET5_0_OR_GREATER
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
#else
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
#endif
{
addresses = Linux.StringParsingHelpers.ParseDnsAddressesFromResolvConfFile(EtcResolvConfFile);
try
{
addresses = Linux.StringParsingHelpers.ParseDnsAddressesFromResolvConfFile(EtcResolvConfFile);
}
catch (Exception e) when (e is FileNotFoundException || e is UnauthorizedAccessException)
{
}
}

return addresses;
Expand All @@ -380,12 +403,7 @@ public static IReadOnlyCollection<NameServer> ResolveNameServersNative()
/// <returns>Returns a collection of name servers from the policy table</returns>
public static IReadOnlyCollection<NameServer> ResolveNameResolutionPolicyServers()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return NameResolutionPolicy.Resolve();
}

return Array.Empty<NameServer>();
return NameResolutionPolicy.Resolve();
}

#endif
Expand Down Expand Up @@ -413,16 +431,20 @@ private static IReadOnlyCollection<NameServer> QueryNetworkInterfaces()
var result = new HashSet<NameServer>();

var adapters = NetworkInterface.GetAllNetworkInterfaces();
if (adapters == null)
{
return result.ToArray();
}

foreach (NetworkInterface networkInterface in
adapters
.Where(p => (p.OperationalStatus == OperationalStatus.Up || p.OperationalStatus == OperationalStatus.Unknown)
.Where(p => p != null && (p.OperationalStatus == OperationalStatus.Up || p.OperationalStatus == OperationalStatus.Unknown)
&& p.NetworkInterfaceType != NetworkInterfaceType.Loopback))
{
var properties = networkInterface.GetIPProperties();
var properties = networkInterface?.GetIPProperties();

// Can be null under mono for whatever reason...
if (properties == null)
if (properties?.DnsAddresses == null)
{
continue;
}
Expand Down
Loading

0 comments on commit 4a8266b

Please sign in to comment.