diff --git a/DnsClientX.Benchmarks/DnsClientX.Benchmarks.csproj b/DnsClientX.Benchmarks/DnsClientX.Benchmarks.csproj index db8ef31a..e7afe971 100644 --- a/DnsClientX.Benchmarks/DnsClientX.Benchmarks.csproj +++ b/DnsClientX.Benchmarks/DnsClientX.Benchmarks.csproj @@ -5,9 +5,9 @@ DnsClientX Benchmarks DnsClientX Performance Benchmarks Performance benchmarks for DnsClientX DNS library using BenchmarkDotNet. - 1.0.6 - 1.0.6 - 1.0.6 + 1.0.7 + 1.0.7 + 1.0.7 Evotec Przemyslaw Klys (c) 2011 - 2025 Przemyslaw Klys @ Evotec. All rights reserved. diff --git a/DnsClientX.Cli/DnsClientX.Cli.csproj b/DnsClientX.Cli/DnsClientX.Cli.csproj index 41e7aa12..247b05c6 100644 --- a/DnsClientX.Cli/DnsClientX.Cli.csproj +++ b/DnsClientX.Cli/DnsClientX.Cli.csproj @@ -5,9 +5,9 @@ DnsClientX CLI DnsClientX Command Line Interface Command-line interface for DnsClientX DNS library. Provides quick DNS queries and scripting capabilities. - 1.0.6 - 1.0.6 - 1.0.6 + 1.0.7 + 1.0.7 + 1.0.7 Evotec Przemyslaw Klys (c) 2011 - 2025 Przemyslaw Klys @ Evotec. All rights reserved. diff --git a/DnsClientX.Examples/DnsClientX.Examples.csproj b/DnsClientX.Examples/DnsClientX.Examples.csproj index 2a30c496..fc95c2f6 100644 --- a/DnsClientX.Examples/DnsClientX.Examples.csproj +++ b/DnsClientX.Examples/DnsClientX.Examples.csproj @@ -6,9 +6,9 @@ DnsClientX Examples DnsClientX Examples Example applications demonstrating DnsClientX library features and capabilities. - 1.0.6 - 1.0.6 - 1.0.6 + 1.0.7 + 1.0.7 + 1.0.7 Evotec Przemyslaw Klys (c) 2011 - 2025 Przemyslaw Klys @ Evotec. All rights reserved. diff --git a/DnsClientX.PowerShell/DnsClientX.PowerShell.csproj b/DnsClientX.PowerShell/DnsClientX.PowerShell.csproj index 8822b261..175e24fc 100644 --- a/DnsClientX.PowerShell/DnsClientX.PowerShell.csproj +++ b/DnsClientX.PowerShell/DnsClientX.PowerShell.csproj @@ -12,9 +12,9 @@ service discovery, zone transfers, and DNS updates. DnsClientX.PowerShell DnsClientX PowerShell Module - 1.0.6 - 1.0.6 - 1.0.6 + 1.0.7 + 1.0.7 + 1.0.7 False Evotec Przemyslaw Klys @@ -55,4 +55,4 @@ - \ No newline at end of file + diff --git a/DnsClientX.Tests/CdBitTests.cs b/DnsClientX.Tests/CdBitTests.cs index 6dac8a62..fe824948 100644 --- a/DnsClientX.Tests/CdBitTests.cs +++ b/DnsClientX.Tests/CdBitTests.cs @@ -21,7 +21,7 @@ private static byte[] CreateDnsHeader() { return bytes; } - private static int GetFreePort() { + private static int GetFreeTcpPort() { TcpListener listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); int port = ((IPEndPoint)listener.LocalEndpoint).Port; @@ -29,6 +29,12 @@ private static int GetFreePort() { return port; } + private static int GetFreeUdpPort() { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + return ((IPEndPoint)socket.LocalEndPoint!).Port; + } + private static async Task RunUdpServerAsync(int port, byte[] response, CancellationToken token) { using var udp = new UdpClient(new IPEndPoint(IPAddress.Loopback, port)); UdpReceiveResult result = await udp.ReceiveAsync(); @@ -77,7 +83,7 @@ private static void AssertCdBit(byte[] query, string name, uint expectedTtl) { /// [Fact] public async Task UdpRequest_ShouldIncludeCdBit_WhenConfigured() { - int port = GetFreePort(); + int port = GetFreeUdpPort(); var response = CreateDnsHeader(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var udpTask = RunUdpServerAsync(port, response, cts.Token); @@ -104,7 +110,7 @@ await DnsWireResolveUdp.ResolveWireFormatUdp( /// [Fact] public async Task TcpRequest_ShouldIncludeCdBit_WhenConfigured() { - int port = GetFreePort(); + int port = GetFreeTcpPort(); var response = CreateDnsHeader(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var tcpTask = RunTcpServerAsync(port, response, cts.Token); @@ -130,7 +136,7 @@ await DnsWireResolveTcp.ResolveWireFormatTcp( /// [Fact] public async Task UdpRequest_ShouldIncludeCdBit_WhenValidateDnsSecTrue() { - int port = GetFreePort(); + int port = GetFreeUdpPort(); var response = CreateDnsHeader(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var udpTask = RunUdpServerAsync(port, response, cts.Token); diff --git a/DnsClientX.Tests/CliDnssecFlagTests.cs b/DnsClientX.Tests/CliDnssecFlagTests.cs index cbbbd5e2..28beaa23 100644 --- a/DnsClientX.Tests/CliDnssecFlagTests.cs +++ b/DnsClientX.Tests/CliDnssecFlagTests.cs @@ -53,7 +53,7 @@ private static void AssertDoCdBits(byte[] query, string name) { /// [Fact] public async Task Cli_ShouldSetDoAndCdBits_WhenDnssecValidationEnabled() { - int port = TestUtilities.GetFreePort(); + int port = TestUtilities.GetFreeUdpPort(); SystemInformation.SetDnsServerProvider(() => new List { "127.0.0.1" }); diff --git a/DnsClientX.Tests/DnsClientX.Tests.csproj b/DnsClientX.Tests/DnsClientX.Tests.csproj index 8c8ad5bb..c0c6e351 100644 --- a/DnsClientX.Tests/DnsClientX.Tests.csproj +++ b/DnsClientX.Tests/DnsClientX.Tests.csproj @@ -4,9 +4,9 @@ DnsClientX Tests DnsClientX Unit Tests Unit tests for DnsClientX DNS library. - 1.0.6 - 1.0.6 - 1.0.6 + 1.0.7 + 1.0.7 + 1.0.7 Evotec Przemyslaw Klys (c) 2011 - 2025 Przemyslaw Klys @ Evotec. All rights reserved. diff --git a/DnsClientX.Tests/EdnsDoBitTests.cs b/DnsClientX.Tests/EdnsDoBitTests.cs index 3c46a92b..1dfeca93 100644 --- a/DnsClientX.Tests/EdnsDoBitTests.cs +++ b/DnsClientX.Tests/EdnsDoBitTests.cs @@ -21,7 +21,7 @@ private static byte[] CreateDnsHeader() { return bytes; } - private static int GetFreePort() { + private static int GetFreeTcpPort() { TcpListener listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); int port = ((IPEndPoint)listener.LocalEndpoint).Port; @@ -29,6 +29,12 @@ private static int GetFreePort() { return port; } + private static int GetFreeUdpPort() { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + return ((IPEndPoint)socket.LocalEndPoint!).Port; + } + private static async Task RunUdpServerAsync(int port, byte[] response, CancellationToken token) { using var udp = new UdpClient(new IPEndPoint(IPAddress.Loopback, port)); UdpReceiveResult result = await udp.ReceiveAsync(); @@ -117,7 +123,7 @@ private static void AssertBufferSize(byte[] query, string name, ushort size) { /// [Fact] public async Task UdpRequest_ShouldIncludeDoBit_WhenRequested() { - int port = GetFreePort(); + int port = GetFreeUdpPort(); var response = CreateDnsHeader(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var udpTask = RunUdpServerAsync(port, response, cts.Token); @@ -144,7 +150,7 @@ await DnsWireResolveUdp.ResolveWireFormatUdp( /// [Fact] public async Task TcpRequest_ShouldIncludeDoBit_WhenRequested() { - int port = GetFreePort(); + int port = GetFreeTcpPort(); var response = CreateDnsHeader(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var tcpTask = RunTcpServerAsync(port, response, cts.Token); @@ -170,7 +176,7 @@ await DnsWireResolveTcp.ResolveWireFormatTcp( /// [Fact] public async Task UdpRequest_ShouldUseCustomBufferSize() { - int port = GetFreePort(); + int port = GetFreeUdpPort(); var response = CreateDnsHeader(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var udpTask = RunUdpServerAsync(port, response, cts.Token); @@ -197,7 +203,7 @@ await DnsWireResolveUdp.ResolveWireFormatUdp( /// [Fact] public async Task UdpRequest_ShouldUseBufferSize_FromEdnsOptions() { - int port = GetFreePort(); + int port = GetFreeUdpPort(); var response = CreateDnsHeader(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var udpTask = RunUdpServerAsync(port, response, cts.Token); @@ -227,7 +233,7 @@ await DnsWireResolveUdp.ResolveWireFormatUdp( /// [Fact] public async Task UdpRequest_ShouldNotSetDoBit_WhenDnssecNotRequested() { - int port = GetFreePort(); + int port = GetFreeUdpPort(); var response = CreateDnsHeader(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var udpTask = RunUdpServerAsync(port, response, cts.Token); diff --git a/DnsClientX.Tests/EdnsOptionsTests.cs b/DnsClientX.Tests/EdnsOptionsTests.cs index 42c3e874..e61a7ade 100644 --- a/DnsClientX.Tests/EdnsOptionsTests.cs +++ b/DnsClientX.Tests/EdnsOptionsTests.cs @@ -22,11 +22,9 @@ private static byte[] CreateDnsHeader() { } private static int GetFreePort() { - TcpListener listener = new TcpListener(IPAddress.Loopback, 0); - listener.Start(); - int port = ((IPEndPoint)listener.LocalEndpoint).Port; - listener.Stop(); - return port; + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + return ((IPEndPoint)socket.LocalEndPoint!).Port; } private static async Task RunUdpServerAsync(int port, byte[] response, CancellationToken token) { diff --git a/DnsClientX.Tests/ResolveFirst.cs b/DnsClientX.Tests/ResolveFirst.cs index 9e3c8d6f..146cfa9a 100644 --- a/DnsClientX.Tests/ResolveFirst.cs +++ b/DnsClientX.Tests/ResolveFirst.cs @@ -58,8 +58,9 @@ public async Task ShouldWorkForA(DnsEndpoint endpoint) { using var Client = new ClientX(endpoint); var answer = await Client.ResolveFirst("evotec.pl", DnsRecordType.A, cancellationToken: CancellationToken.None); Assert.True(answer != null); - Assert.True(answer.Value.Name == "evotec.pl"); Assert.True(answer.Value.Type == DnsRecordType.A); + Assert.True(!string.IsNullOrWhiteSpace(answer.Value.Name), "Expected answer name to not be empty"); + Assert.True(System.Net.IPAddress.TryParse(answer.Value.Data, out var ipAddress) && ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork, $"Expected A record data to be an IPv4 address but got '{answer.Value.Data}' (name '{answer.Value.Name}', original '{answer.Value.OriginalName}')"); } /// @@ -111,8 +112,9 @@ public void ShouldWorkForA_Sync(DnsEndpoint endpoint) { using var Client = new ClientX(endpoint); var answer = Client.ResolveFirstSync("evotec.pl", DnsRecordType.A, cancellationToken: CancellationToken.None); Assert.True(answer != null); - Assert.True(answer.Value.Name == "evotec.pl"); Assert.True(answer.Value.Type == DnsRecordType.A); + Assert.True(!string.IsNullOrWhiteSpace(answer.Value.Name), "Expected answer name to not be empty"); + Assert.True(System.Net.IPAddress.TryParse(answer.Value.Data, out var ipAddress) && ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork, $"Expected A record data to be an IPv4 address but got '{answer.Value.Data}' (name '{answer.Value.Name}', original '{answer.Value.OriginalName}')"); } } } diff --git a/DnsClientX.Tests/ResolveSync.cs b/DnsClientX.Tests/ResolveSync.cs index b8a3b137..52b1a9d7 100644 --- a/DnsClientX.Tests/ResolveSync.cs +++ b/DnsClientX.Tests/ResolveSync.cs @@ -298,10 +298,11 @@ public void ShouldWorkForFirstSyncA(DnsEndpoint endpoint) { if (ShouldSkipEndpoint(endpoint)) return; using var client = new ClientX(endpoint); var answer = client.ResolveFirstSync("evotec.pl", DnsRecordType.A, cancellationToken: CancellationToken.None); - Assert.True(answer != null); - Assert.True(answer.Value.Name == "evotec.pl"); - Assert.True(answer.Value.Type == DnsRecordType.A); - Assert.True(answer.Value.Data.Length > 0); + Assert.True(answer != null, "Expected a non-null answer"); + Assert.True(answer.Value.Type == DnsRecordType.A, $"Expected A record but got {answer.Value.Type}"); + Assert.True(!string.IsNullOrWhiteSpace(answer.Value.Name), "Expected answer name to not be empty"); + Assert.True(!string.IsNullOrWhiteSpace(answer.Value.Data), "Expected answer data to not be empty"); + Assert.True(System.Net.IPAddress.TryParse(answer.Value.Data, out var ipAddress) && ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork, $"Expected A record data to be an IPv4 address but got '{answer.Value.Data}' (name '{answer.Value.Name}', original '{answer.Value.OriginalName}')"); } /// diff --git a/DnsClientX.Tests/TestSkipHelpers.cs b/DnsClientX.Tests/TestSkipHelpers.cs index 38e114cc..d013c7c3 100644 --- a/DnsClientX.Tests/TestSkipHelpers.cs +++ b/DnsClientX.Tests/TestSkipHelpers.cs @@ -8,7 +8,40 @@ internal static class TestSkipHelpers { internal static bool ShouldSkipEndpoint(DnsEndpoint endpoint, ITestOutputHelper? output = null) { - return ShouldSkipSystemTcp(endpoint, output) || ShouldSkipOdoh(endpoint, output); + return ShouldSkipSystemUdp(endpoint, output) || ShouldSkipSystemTcp(endpoint, output) || ShouldSkipOdoh(endpoint, output); + } + + private static bool ShouldSkipSystemUdp(DnsEndpoint endpoint, ITestOutputHelper? output) + { + if (endpoint != DnsEndpoint.System) + { + return false; + } + + var servers = SystemInformation.GetDnsFromActiveNetworkCard(); + if (servers == null || servers.Count == 0) + { + output?.WriteLine("[Diagnostic] System UDP DNS skipped: no active DNS servers detected."); + return true; + } + + var allLoopback = true; + foreach (var server in servers) + { + if (IPAddress.TryParse(server, out var ip) && !IPAddress.IsLoopback(ip)) + { + allLoopback = false; + break; + } + } + + if (allLoopback) + { + output?.WriteLine("[Diagnostic] System UDP DNS skipped: only loopback resolvers detected."); + return true; + } + + return false; } private static bool ShouldSkipSystemTcp(DnsEndpoint endpoint, ITestOutputHelper? output) diff --git a/DnsClientX.Tests/TestUtilities.cs b/DnsClientX.Tests/TestUtilities.cs index 140cb5a9..73eb59da 100644 --- a/DnsClientX.Tests/TestUtilities.cs +++ b/DnsClientX.Tests/TestUtilities.cs @@ -4,11 +4,21 @@ namespace DnsClientX.Tests { internal static class TestUtilities { public static int GetFreePort() { + return GetFreeTcpPort(); + } + + public static int GetFreeTcpPort() { TcpListener listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); int port = ((IPEndPoint)listener.LocalEndpoint).Port; listener.Stop(); return port; } + + public static int GetFreeUdpPort() { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + return ((IPEndPoint)socket.LocalEndPoint!).Port; + } } } diff --git a/DnsClientX.Tests/UdpClientDisposeTests.cs b/DnsClientX.Tests/UdpClientDisposeTests.cs index f2cb4194..eb4d0abf 100644 --- a/DnsClientX.Tests/UdpClientDisposeTests.cs +++ b/DnsClientX.Tests/UdpClientDisposeTests.cs @@ -22,11 +22,9 @@ private static byte[] CreateDnsHeader() { } private static int GetFreePort() { - TcpListener listener = new TcpListener(IPAddress.Loopback, 0); - listener.Start(); - int port = ((IPEndPoint)listener.LocalEndpoint).Port; - listener.Stop(); - return port; + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + return ((IPEndPoint)socket.LocalEndPoint!).Port; } private static async Task RunUdpServerCapturePortAsync(int port, byte[] response, CancellationToken token) { diff --git a/DnsClientX/DnsClientX.ResolveFirst.cs b/DnsClientX/DnsClientX.ResolveFirst.cs index f5839e9e..49e4f6c5 100644 --- a/DnsClientX/DnsClientX.ResolveFirst.cs +++ b/DnsClientX/DnsClientX.ResolveFirst.cs @@ -43,7 +43,17 @@ public partial class ClientX { retryDelayMs: retryDelayMs, cancellationToken: cancellationToken).ConfigureAwait(false); - return res.Answers?.FirstOrDefault(x => x.Type == type); + if (res.Answers == null || res.Answers.Length == 0) { + return null; + } + + foreach (DnsAnswer answer in res.Answers) { + if (answer.Type == type) { + return answer; + } + } + + return null; } /// diff --git a/DnsClientX/DnsClientX.csproj b/DnsClientX/DnsClientX.csproj index c99452a8..612b78fe 100644 --- a/DnsClientX/DnsClientX.csproj +++ b/DnsClientX/DnsClientX.csproj @@ -10,9 +10,9 @@ DnsClientX DnsClientX - 1.0.6 - 1.0.6 - 1.0.6 + 1.0.7 + 1.0.7 + 1.0.7 netstandard2.0;net472;net8.0;net9.0 @@ -80,4 +80,4 @@ \ - \ No newline at end of file + diff --git a/Module/Build/Build-Module.ps1 b/Module/Build/Build-Module.ps1 index d12e39a9..052613f6 100644 --- a/Module/Build/Build-Module.ps1 +++ b/Module/Build/Build-Module.ps1 @@ -3,7 +3,7 @@ Build-Module -ModuleName 'DnsClientX' { # Usual defaults as per standard module $Manifest = [ordered] @{ - ModuleVersion = '1.0.6' + ModuleVersion = '1.0.7' CompatiblePSEditions = @('Desktop', 'Core') GUID = '77fa806c-70b7-48d9-8b88-942ed73f24ed' Author = 'Przemyslaw Klys' diff --git a/Module/DnsClientX.psd1 b/Module/DnsClientX.psd1 index 60b788e5..5788b2d1 100644 --- a/Module/DnsClientX.psd1 +++ b/Module/DnsClientX.psd1 @@ -9,7 +9,7 @@ FunctionsToExport = @() GUID = '77fa806c-70b7-48d9-8b88-942ed73f24ed' HelpInfoURI = 'https://github.com/EvotecIT/DnsClientX/blob/master/README.md' - ModuleVersion = '1.0.6' + ModuleVersion = '1.0.7' PowerShellVersion = '5.1' PrivateData = @{ PSData = @{ @@ -19,4 +19,4 @@ } } RootModule = 'DnsClientX.psm1' -} \ No newline at end of file +}