diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs
index d06ce8117fc5a2..8de1e2a0f4ce31 100644
--- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs
+++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs
@@ -7,12 +7,8 @@
namespace System.Net
{
- internal static class SocketProtocolSupportPal
+ internal static partial class SocketProtocolSupportPal
{
- public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6);
- public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork);
- public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix);
-
private static unsafe bool IsSupported(AddressFamily af)
{
IntPtr invalid = (IntPtr)(-1);
diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
index de0465a51c6c7e..50e7db176e2f14 100644
--- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
+++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs
@@ -9,12 +9,8 @@
namespace System.Net
{
- internal static class SocketProtocolSupportPal
+ internal static partial class SocketProtocolSupportPal
{
- public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6);
- public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork);
- public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix);
-
private static bool IsSupported(AddressFamily af)
{
Interop.Winsock.EnsureInitialized();
diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs
new file mode 100644
index 00000000000000..a61f47a0fa458d
--- /dev/null
+++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Sockets;
+
+namespace System.Net
+{
+ internal static partial class SocketProtocolSupportPal
+ {
+ private const string DisableIPv6AppCtxSwitch = "System.Net.DisableIPv6";
+ private const string DisableIPv6EnvironmentVariable = "DOTNET_SYSTEM_NET_DISABLEIPV6";
+
+ public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6) && !IsIPv6Disabled();
+ public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork);
+ public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix);
+
+ private static bool IsIPv6Disabled()
+ {
+ // First check for the AppContext switch, giving it priority over the environment variable.
+ if (AppContext.TryGetSwitch(DisableIPv6AppCtxSwitch, out bool disabled))
+ {
+ return disabled;
+ }
+
+ // AppContext switch wasn't used. Check the environment variable.
+ string? envVar = Environment.GetEnvironmentVariable(DisableIPv6EnvironmentVariable);
+
+ if (envVar is not null)
+ {
+ return envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj b/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj
index d2d9b9b46910cc..1642f1fff33dda 100644
--- a/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj
+++ b/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj
@@ -31,6 +31,8 @@
Link="Common\System\Net\IPAddressParserStatics.cs" />
+
diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs
index 46858edea5dfab..74c62684a82c5b 100644
--- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs
+++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs
@@ -76,9 +76,11 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool
Interop.Sys.IPAddress* addressHandle = hostEntry.IPAddressList;
for (int i = 0; i < hostEntry.IPAddressCount; i++)
{
- if (Array.IndexOf(nativeAddresses, addressHandle[i], 0, nativeAddressCount) == -1)
+ Interop.Sys.IPAddress nativeAddr = addressHandle[i];
+ if (Array.IndexOf(nativeAddresses, nativeAddr, 0, nativeAddressCount) == -1 &&
+ (!nativeAddr.IsIPv6 || SocketProtocolSupportPal.OSSupportsIPv6)) // Do not include IPv6 addresses if IPV6 support is force-disabled
{
- nativeAddresses[nativeAddressCount++] = addressHandle[i];
+ nativeAddresses[nativeAddressCount++] = nativeAddr;
}
}
diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs
index f860a2274e8187..7a5d4c899e9218 100644
--- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs
+++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs
@@ -6,7 +6,7 @@
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
-
+using Microsoft.DotNet.RemoteExecutor;
using Microsoft.DotNet.XUnitExtensions;
using Xunit;
@@ -21,9 +21,16 @@ public async Task Dns_GetHostEntryAsync_IPAddress_Ok()
await TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(localIPAddress));
}
- [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")]
+
+ public static bool GetHostEntryWorks =
+ // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")]
+ PlatformDetection.IsNotArmNorArm64Process &&
+ // [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)]
+ PlatformDetection.IsNotOSX &&
+ // [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
+ !PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst;
+
+ [ConditionalTheory(nameof(GetHostEntryWorks))]
[InlineData("")]
[InlineData(TestSettings.LocalHost)]
public async Task Dns_GetHostEntry_HostString_Ok(string hostName)
@@ -77,12 +84,10 @@ public async Task Dns_GetHostEntry_HostString_Ok(string hostName)
}
}
- [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")]
+ [ConditionalTheory(nameof(GetHostEntryWorks))]
[InlineData("")]
[InlineData(TestSettings.LocalHost)]
- public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName)
+ public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName)
{
if (PlatformDetection.IsSLES)
{
@@ -112,6 +117,44 @@ private static async Task TestGetHostEntryAsync(Func> getHostE
Assert.Equal(list1, list2);
}
+ public static bool GetHostEntry_DisableIPv6_Condition = GetHostEntryWorks && RemoteExecutor.IsSupported;
+
+ [ConditionalTheory(nameof(GetHostEntry_DisableIPv6_Condition))]
+ [InlineData("")]
+ [InlineData(TestSettings.LocalHost)]
+ public void Dns_GetHostEntry_DisableIPv6_ExcludesIPv6Addresses(string hostnameOuter)
+ {
+ RemoteExecutor.Invoke(RunTest, hostnameOuter).Dispose();
+
+ static void RunTest(string hostnameInner)
+ {
+ AppContext.SetSwitch("System.Net.DisableIPv6", true);
+ IPHostEntry entry = Dns.GetHostEntry(hostnameInner);
+ foreach (IPAddress address in entry.AddressList)
+ {
+ Assert.NotEqual(AddressFamily.InterNetworkV6, address.AddressFamily);
+ }
+ }
+ }
+
+ [ConditionalTheory(nameof(GetHostEntry_DisableIPv6_Condition))]
+ [InlineData("")]
+ [InlineData(TestSettings.LocalHost)]
+ public void Dns_GetHostEntryAsync_DisableIPv6_ExcludesIPv6Addresses(string hostnameOuter)
+ {
+ RemoteExecutor.Invoke(RunTest, hostnameOuter).Dispose();
+
+ static async Task RunTest(string hostnameInner)
+ {
+ AppContext.SetSwitch("System.Net.DisableIPv6", true);
+ IPHostEntry entry = await Dns.GetHostEntryAsync(hostnameInner);
+ foreach (IPAddress address in entry.AddressList)
+ {
+ Assert.NotEqual(AddressFamily.InterNetworkV6, address.AddressFamily);
+ }
+ }
+ }
+
[Fact]
public async Task Dns_GetHostEntry_NullStringHost_Fail()
{
diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj
index 7b2b9ee2c9fd60..3b9a81c4ee5d35 100644
--- a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj
+++ b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj
@@ -28,6 +28,8 @@
Link="Common\System\Net\IPEndPointStatics.cs" />
+
+
diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj
index 35d10bb679f163..496c628956f8c1 100644
--- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj
+++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj
@@ -65,6 +65,8 @@
Link="Common\System\Net\SocketAddress.cs" />
+
diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs
index 3e2886baf52b7e..9ed625aecd0ffc 100644
--- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs
+++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/OSSupport.cs
@@ -3,6 +3,7 @@
using System.Threading;
+using Microsoft.DotNet.RemoteExecutor;
using Xunit;
namespace System.Net.Sockets.Tests
@@ -25,6 +26,37 @@ public void SupportsIPv6_MatchesOSSupportsIPv6()
#pragma warning restore
}
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void DisableIPv6_OSSupportsIPv6_False()
+ {
+ RemoteInvokeOptions options = new RemoteInvokeOptions();
+ options.StartInfo.EnvironmentVariables["DOTNET_SYSTEM_NET_DISABLEIPV6"] = "1";
+ RemoteExecutor.Invoke(RunTest, options).Dispose();
+
+ static void RunTest()
+ {
+ Assert.False(Socket.OSSupportsIPv6);
+ }
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void DisableIPv6_SocketConstructor_CreatesIPv4Socket()
+ {
+ RemoteExecutor.Invoke(RunTest).Dispose();
+
+ static void RunTest()
+ {
+ AppContext.SetSwitch("System.Net.DisableIPv6", true);
+ using Socket socket1 = new Socket(SocketType.Stream, ProtocolType.Tcp);
+ using Socket socket2 = new Socket(SocketType.Dgram, ProtocolType.Udp);
+
+ Assert.Equal(AddressFamily.InterNetwork, socket1.AddressFamily);
+ Assert.Equal(AddressFamily.InterNetwork, socket2.AddressFamily);
+ Assert.False(socket1.DualMode);
+ Assert.False(socket2.DualMode);
+ }
+ }
+
[Fact]
public void IOControl_FIONREAD_Success()
{