From ef57c0a74b58d481d9de1502a9c19ac6b3993911 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 12:01:51 +0000 Subject: [PATCH 1/2] Initial plan From 29932fb19d7793a36ecd4b62c84a109c0734509e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 12:26:18 +0000 Subject: [PATCH 2/2] Enable localhost loopback assertions on Apple mobile and Android Co-authored-by: kotlarmilos <11523312+kotlarmilos@users.noreply.github.com> --- .../FunctionalTests/GetHostAddressesTest.cs | 45 ++++--------------- .../tests/FunctionalTests/GetHostEntryTest.cs | 45 ++++--------------- 2 files changed, 16 insertions(+), 74 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs index 249aa22ef43c49..69250970e0b6f3 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostAddressesTest.cs @@ -235,23 +235,13 @@ public async Task DnsGetHostAddresses_LocalhostSubdomain_ReturnsLoopback(string { // The subdomain goes to OS resolver first. If it fails (likely on most systems), // it falls back to resolving plain "localhost", which should return loopback addresses. - // On Android/Apple mobile platforms the OS resolver may return non-loopback addresses - // for *.localhost. - bool requireLoopback = !PlatformDetection.IsAppleMobile && !PlatformDetection.IsAndroid; - IPAddress[] addresses = Dns.GetHostAddresses(hostName); Assert.True(addresses.Length >= 1, "Expected at least one address"); - if (requireLoopback) - { - Assert.All(addresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(addresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); addresses = await Dns.GetHostAddressesAsync(hostName); Assert.True(addresses.Length >= 1, "Expected at least one address"); - if (requireLoopback) - { - Assert.All(addresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(addresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); } // RFC 6761: "*.localhost" subdomains should respect AddressFamily parameter. @@ -291,35 +281,24 @@ public async Task DnsGetHostAddresses_LocalhostSubdomain_RespectsAddressFamily(A // 1. The OS resolver is tried first for subdomains // 2. The OS may return different results (e.g., both IPv4+IPv6 vs IPv4 only) // 3. Different systems configure localhost differently - // On Android and Apple mobile the OS resolver may return non-loopback addresses - // for both plain "localhost" and "*.localhost" (e.g. link-local IPv6 or - // multicast DNS results), so we only require any address to be returned there. [Fact] public async Task DnsGetHostAddresses_LocalhostAndSubdomain_BothReturnLoopback() { - bool requireLoopback = !PlatformDetection.IsAppleMobile && !PlatformDetection.IsAndroid; - IPAddress[] localhostAddresses = Dns.GetHostAddresses("localhost"); IPAddress[] subdomainAddresses = Dns.GetHostAddresses("foo.localhost"); Assert.True(localhostAddresses.Length >= 1); Assert.True(subdomainAddresses.Length >= 1); - if (requireLoopback) - { - Assert.All(localhostAddresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - Assert.All(subdomainAddresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(localhostAddresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); + Assert.All(subdomainAddresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); localhostAddresses = await Dns.GetHostAddressesAsync("localhost"); subdomainAddresses = await Dns.GetHostAddressesAsync("bar.localhost"); Assert.True(localhostAddresses.Length >= 1); Assert.True(subdomainAddresses.Length >= 1); - if (requireLoopback) - { - Assert.All(localhostAddresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - Assert.All(subdomainAddresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(localhostAddresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); + Assert.All(subdomainAddresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); } // RFC 6761: Localhost subdomains with trailing dot should work (e.g., "foo.localhost.") @@ -328,21 +307,13 @@ public async Task DnsGetHostAddresses_LocalhostAndSubdomain_BothReturnLoopback() [InlineData("bar.test.localhost.")] public async Task DnsGetHostAddresses_LocalhostSubdomainWithTrailingDot_ReturnsLoopback(string hostName) { - bool requireLoopback = !PlatformDetection.IsAppleMobile && !PlatformDetection.IsAndroid; - IPAddress[] addresses = Dns.GetHostAddresses(hostName); Assert.True(addresses.Length >= 1, "Expected at least one address"); - if (requireLoopback) - { - Assert.All(addresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(addresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); addresses = await Dns.GetHostAddressesAsync(hostName); Assert.True(addresses.Length >= 1, "Expected at least one address"); - if (requireLoopback) - { - Assert.All(addresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(addresses, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); } // RFC 6761: Ensure names that look similar but are not reserved are still resolved via OS. diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs index d3d1676c9b110f..811bb1eb16846c 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs @@ -357,23 +357,13 @@ public async Task DnsGetHostEntry_LocalhostSubdomain_ReturnsLoopback(string host { // The subdomain goes to OS resolver first. If it fails (likely on most systems), // it falls back to resolving plain "localhost", which should return loopback addresses. - // On Android/Apple mobile platforms the OS resolver may return non-loopback addresses - // for *.localhost. - bool requireLoopback = !PlatformDetection.IsAppleMobile && !PlatformDetection.IsAndroid; - IPHostEntry entry = Dns.GetHostEntry(hostName); Assert.True(entry.AddressList.Length >= 1, "Expected at least one address"); - if (requireLoopback) - { - Assert.All(entry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(entry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); entry = await Dns.GetHostEntryAsync(hostName); Assert.True(entry.AddressList.Length >= 1, "Expected at least one address"); - if (requireLoopback) - { - Assert.All(entry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(entry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); } // RFC 6761: Ensure names that look similar but are not reserved are still resolved via OS. @@ -456,35 +446,24 @@ public async Task DnsGetHostEntry_LocalhostSubdomain_RespectsAddressFamily(Addre // 1. The OS resolver is tried first for subdomains // 2. The OS may return different results (e.g., both IPv4+IPv6 vs IPv4 only) // 3. Different systems configure localhost differently - // On Android and Apple mobile the OS resolver may return non-loopback addresses - // for both plain "localhost" and "*.localhost" (e.g. link-local IPv6 or - // multicast DNS results), so we only require any address to be returned there. [Fact] public async Task DnsGetHostEntry_LocalhostAndSubdomain_BothReturnLoopback() { - bool requireLoopback = !PlatformDetection.IsAppleMobile && !PlatformDetection.IsAndroid; - IPHostEntry localhostEntry = Dns.GetHostEntry("localhost"); IPHostEntry subdomainEntry = Dns.GetHostEntry("foo.localhost"); Assert.True(localhostEntry.AddressList.Length >= 1); Assert.True(subdomainEntry.AddressList.Length >= 1); - if (requireLoopback) - { - Assert.All(localhostEntry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - Assert.All(subdomainEntry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(localhostEntry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); + Assert.All(subdomainEntry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); localhostEntry = await Dns.GetHostEntryAsync("localhost"); subdomainEntry = await Dns.GetHostEntryAsync("bar.localhost"); Assert.True(localhostEntry.AddressList.Length >= 1); Assert.True(subdomainEntry.AddressList.Length >= 1); - if (requireLoopback) - { - Assert.All(localhostEntry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - Assert.All(subdomainEntry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(localhostEntry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); + Assert.All(subdomainEntry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); } // RFC 6761: Localhost subdomains with trailing dot should work (e.g., "foo.localhost.") @@ -494,21 +473,13 @@ public async Task DnsGetHostEntry_LocalhostAndSubdomain_BothReturnLoopback() [InlineData("bar.test.localhost.")] public async Task DnsGetHostEntry_LocalhostSubdomainWithTrailingDot_ReturnsLoopback(string hostName) { - bool requireLoopback = !PlatformDetection.IsAppleMobile && !PlatformDetection.IsAndroid; - IPHostEntry entry = Dns.GetHostEntry(hostName); Assert.True(entry.AddressList.Length >= 1, "Expected at least one address"); - if (requireLoopback) - { - Assert.All(entry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(entry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); entry = await Dns.GetHostEntryAsync(hostName); Assert.True(entry.AddressList.Length >= 1, "Expected at least one address"); - if (requireLoopback) - { - Assert.All(entry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); - } + Assert.All(entry.AddressList, addr => Assert.True(IPAddress.IsLoopback(addr), $"Expected loopback address but got: {addr}")); } [Fact]