Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WASI] System.Net.NameResolution #107351

Merged
merged 19 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ internal static partial class Sys

internal static unsafe string GetHostName()
{
if (OperatingSystem.IsWasi())
{
return "localhost";
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it feels slightly wrong doing this special case in the Interop.GetHostName.cs managed binding layer, but calling into native code just for getting a constant string feels wrong too.

Btw. after looking at it some more I think we have an issue on Browser: we do use emscripten's gethostname() which returns the string emscripten and we just paper over it here:

// In the mono runtime, this maps to gethostname, which returns 'emscripten'.
// Returning the value here allows us to exclude more of the runtime.
public static string MachineName => "localhost";

But Interop.Sys.GetHostName() is used in more places and those would get the "wrong" hostname on Browser. I think I'd be in favor of unifying this and adding the special case for Browser here too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do separate PR for that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


const int HOST_NAME_MAX = 255;
const int ArrLength = HOST_NAME_MAX + 1;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal enum GetAddrInfoErrorFlags : int
EAI_BADARG = 6, // One or more input arguments were invalid.
EAI_NOMORE = 7, // No more entries are present in the list.
EAI_MEMORY = 8, // Out of memory.
EAI_SYSTEM = 9, // Other system error; errno is set to indicate the error.
}

[StructLayout(LayoutKind.Sequential)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<PropertyGroup>
<StrongNameKeyId>Microsoft</StrongNameKeyId>
<IncludePlatformAttributes>true</IncludePlatformAttributes>
<!-- WASI until https://github.com/dotnet/runtime/issues/98957 -->
<UnsupportedOSPlatforms>browser;wasi</UnsupportedOSPlatforms>
<UnsupportedOSPlatforms>browser</UnsupportedOSPlatforms>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
<PropertyGroup>
<TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatformIdentifier>
<GeneratePlatformNotSupportedAssemblyMessage Condition="'$(TargetPlatformIdentifier)' == 'browser' or '$(TargetPlatformIdentifier)' == 'wasi' or '$(TargetPlatformIdentifier)' == ''">SR.SystemNetNameResolution_PlatformNotSupported</GeneratePlatformNotSupportedAssemblyMessage>
<ApiExclusionListPath Condition="'$(TargetPlatformIdentifier)' == 'browser' or '$(TargetPlatformIdentifier)' == 'wasi'">ExcludeApiList.PNSE.Browser.txt</ApiExclusionListPath>
<GeneratePlatformNotSupportedAssemblyMessage Condition="'$(TargetPlatformIdentifier)' == 'browser' or '$(TargetPlatformIdentifier)' == ''">SR.SystemNetNameResolution_PlatformNotSupported</GeneratePlatformNotSupportedAssemblyMessage>
<ApiExclusionListPath Condition="'$(TargetPlatformIdentifier)' == 'browser'">ExcludeApiList.PNSE.Browser.txt</ApiExclusionListPath>
</PropertyGroup>

<ItemGroup Condition="'$(GeneratePlatformNotSupportedAssemblyMessage)' == ''">
<Compile Include="System\Net\Dns.cs" />
<Compile Include="System\Net\IPHostEntry.cs" />
<Compile Include="System\Net\NetEventSource.NameResolution.cs" />
<Compile Include="System\Net\NetEventSource.NameResolution.cs" Condition="'$(TargetPlatformIdentifier)' != 'wasi'" />
<Compile Include="System\Net\NameResolutionMetrics.cs" />
<Compile Include="System\Net\NameResolutionTelemetry.cs" />
<!-- Logging -->
Expand Down Expand Up @@ -80,6 +80,8 @@
Link="Common\Interop\CoreLib\Unix\Interop.Errors.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
Link="Common\Interop\Unix\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ErrNo.cs"
Link="Common\Interop\Unix\System.Native\Interop.ErrNo.cs"/>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Close.cs"
Link="Common\Interop\Unix\System.Native\Interop.Close.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetHostName.cs"
Expand All @@ -96,10 +98,41 @@
Link="Common\Interop\Unix\System.Native\Interop.SocketAddress.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'browser' or '$(TargetPlatformIdentifier)' == 'wasi'">
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'browser'">
<Compile Include="System\Net\Dns.Browser.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'wasi'">
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetHostName.cs"
Link="Common\Interop\Unix\System.Native\Interop.GetHostName.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ErrNo.cs"
Link="Common\Interop\Unix\System.Native\Interop.ErrNo.cs"/>

<Compile Include="System\Net\NameResolutionPal.Unix.cs" />
<Compile Include="$(CommonPath)System\Net\InteropIPAddressExtensions.Unix.cs"
Link="Common\System\Net\InteropIPAddressExtensions.Unix.cs" />
<Compile Include="$(CommonPath)System\Net\SocketAddressPal.Unix.cs"
Link="Common\System\Net\Internals\SocketAddressPal.Unix.cs" />
<Compile Include="$(CommonPath)System\Net\SocketProtocolSupportPal.Unix.cs"
Link="Common\System\Net\SocketProtocolSupportPal.Unix" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Errors.cs"
Link="Common\Interop\CoreLib\Unix\Interop.Errors.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
Link="Common\Interop\Unix\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Close.cs"
Link="Common\Interop\Unix\System.Native\Interop.Close.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetNameInfo.cs"
Link="Common\Interop\Unix\System.Native\Interop.GetNameInfo.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.HostEntry.cs"
Link="Common\Interop\Unix\System.Native\Interop.HostEntries.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.IPAddress.cs"
Link="Common\Interop\Unix\System.Native\Interop.IPAddress.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Socket.cs"
Link="Common\Interop\Unix\System.Native\Interop.Socket.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SocketAddress.cs"
Link="Common\Interop\Unix\System.Native\Interop.SocketAddress.cs" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.Win32.Primitives" />
<Reference Include="System.Collections" />
Expand Down
27 changes: 24 additions & 3 deletions src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Versioning;

namespace System.Net
{
Expand Down Expand Up @@ -37,6 +38,8 @@ public static string GetHostName()

public static IPHostEntry GetHostEntry(IPAddress address)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

ArgumentNullException.ThrowIfNull(address);

if (address.Equals(IPAddress.Any) || address.Equals(IPAddress.IPv6Any))
Expand Down Expand Up @@ -68,7 +71,7 @@ public static IPHostEntry GetHostEntry(string hostNameOrAddress, AddressFamily f

// See if it's an IP Address.
IPHostEntry ipHostEntry;
if (IPAddress.TryParse(hostNameOrAddress, out IPAddress? address))
if (NameResolutionPal.SupportsGetNameInfo && IPAddress.TryParse(hostNameOrAddress, out IPAddress? address))
{
if (address.Equals(IPAddress.Any) || address.Equals(IPAddress.IPv6Any))
{
Expand Down Expand Up @@ -147,6 +150,8 @@ public static Task<IPHostEntry> GetHostEntryAsync(string hostNameOrAddress, Addr

public static Task<IPHostEntry> GetHostEntryAsync(IPAddress address)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

ArgumentNullException.ThrowIfNull(address);

if (address.Equals(IPAddress.Any) || address.Equals(IPAddress.IPv6Any))
Expand All @@ -156,6 +161,8 @@ public static Task<IPHostEntry> GetHostEntryAsync(IPAddress address)
}

return RunAsync(static (s, activity) => {
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185
IPHostEntry ipHostEntry = GetHostEntryCore((IPAddress)s, AddressFamily.Unspecified, activity);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info((IPAddress)s, $"{ipHostEntry} with {ipHostEntry.AddressList.Length} entries");
return ipHostEntry;
Expand All @@ -170,6 +177,8 @@ public static IAsyncResult BeginGetHostEntry(string hostNameOrAddress, AsyncCall

public static IPHostEntry EndGetHostEntry(IAsyncResult asyncResult)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

ArgumentNullException.ThrowIfNull(asyncResult);

return TaskToAsyncResult.End<IPHostEntry>(asyncResult);
Expand Down Expand Up @@ -244,6 +253,8 @@ public static IAsyncResult BeginGetHostAddresses(string hostNameOrAddress, Async

public static IPAddress[] EndGetHostAddresses(IAsyncResult asyncResult)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

ArgumentNullException.ThrowIfNull(asyncResult);

return TaskToAsyncResult.End<IPAddress[]>(asyncResult);
Expand All @@ -269,6 +280,8 @@ public static IAsyncResult BeginGetHostByName(string hostName, AsyncCallback? re
[Obsolete("EndGetHostByName has been deprecated. Use EndGetHostEntry instead.")]
public static IPHostEntry EndGetHostByName(IAsyncResult asyncResult)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

ArgumentNullException.ThrowIfNull(asyncResult);

return TaskToAsyncResult.End<IPHostEntry>(asyncResult);
Expand All @@ -277,6 +290,8 @@ public static IPHostEntry EndGetHostByName(IAsyncResult asyncResult)
[Obsolete("GetHostByAddress has been deprecated. Use GetHostEntry instead.")]
public static IPHostEntry GetHostByAddress(string address)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

ArgumentNullException.ThrowIfNull(address);

IPHostEntry ipHostEntry = GetHostEntryCore(IPAddress.Parse(address), AddressFamily.Unspecified);
Expand All @@ -288,6 +303,8 @@ public static IPHostEntry GetHostByAddress(string address)
[Obsolete("GetHostByAddress has been deprecated. Use GetHostEntry instead.")]
public static IPHostEntry GetHostByAddress(IPAddress address)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

ArgumentNullException.ThrowIfNull(address);

IPHostEntry ipHostEntry = GetHostEntryCore(address, AddressFamily.Unspecified);
Expand All @@ -303,7 +320,7 @@ public static IPHostEntry Resolve(string hostName)

// See if it's an IP Address.
IPHostEntry ipHostEntry;
if (IPAddress.TryParse(hostName, out IPAddress? address) &&
if (NameResolutionPal.SupportsGetNameInfo && IPAddress.TryParse(hostName, out IPAddress? address) &&
(address.AddressFamily != AddressFamily.InterNetworkV6 || SocketProtocolSupportPal.OSSupportsIPv6))
{
try
Expand Down Expand Up @@ -332,6 +349,8 @@ public static IAsyncResult BeginResolve(string hostName, AsyncCallback? requestC
[Obsolete("EndResolve has been deprecated. Use EndGetHostEntry instead.")]
public static IPHostEntry EndResolve(IAsyncResult asyncResult)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

IPHostEntry ipHostEntry;

try
Expand Down Expand Up @@ -414,6 +433,8 @@ private static IPAddress[] GetHostAddressesCore(IPAddress address, AddressFamily
// Does internal IPAddress reverse and then forward lookups (for Legacy and current public methods).
private static object GetHostEntryOrAddressesCore(IPAddress address, bool justAddresses, AddressFamily addressFamily, NameResolutionActivity? activityOrDefault = default)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

// Try to get the data for the host from its address.
// We need to call getnameinfo first, because getaddrinfo w/ the ipaddress string
// will only return that address and not the full list.
Expand Down Expand Up @@ -500,7 +521,7 @@ private static Task GetHostEntryOrAddressesCoreAsync(string hostName, bool justR
object asyncState;

// See if it's an IP Address.
if (IPAddress.TryParse(hostName, out IPAddress? ipAddress))
if (NameResolutionPal.SupportsGetNameInfo && IPAddress.TryParse(hostName, out IPAddress? ipAddress))
{
if (throwOnIIPAny && (ipAddress.Equals(IPAddress.Any) || ipAddress.Equals(IPAddress.IPv6Any)))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Versioning;

namespace System.Net
{
internal static partial class NameResolutionPal
{
public const bool SupportsGetAddrInfoAsync = false;

[UnsupportedOSPlatformGuard("wasi")]
public static bool SupportsGetNameInfo => !OperatingSystem.IsWasi();

#pragma warning disable IDE0060
internal static Task? GetAddrInfoAsync(string hostName, bool justAddresses, AddressFamily family, CancellationToken cancellationToken) =>
throw new NotSupportedException();
Expand All @@ -39,6 +43,9 @@ private static SocketError GetSocketErrorForNativeError(int error)
return SocketError.HostNotFound;
case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_MEMORY:
throw new OutOfMemoryException();
case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_SYSTEM:
Debug.Fail($"Unexpected error: {error} errno: {Interop.Sys.GetErrNo()}");
return SocketError.SocketError;
default:
Debug.Fail($"Unexpected error: {error}");
return SocketError.SocketError;
Expand Down Expand Up @@ -146,6 +153,8 @@ public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses,

public static unsafe string? TryGetNameInfo(IPAddress addr, out SocketError socketError, out int nativeErrorCode)
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185

byte* buffer = stackalloc byte[Interop.Sys.NI_MAXHOST + 1 /*for null*/];

byte isIPv6;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
using System.Runtime.Versioning;

namespace System.Net
{
Expand Down Expand Up @@ -43,6 +44,8 @@ static void Initialize()
}
}

public const bool SupportsGetNameInfo = true;

public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, AddressFamily addressFamily, out string? hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode)
{
Interop.Winsock.EnsureInitialized();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected override void OnEventCommand(EventCommandEventArgs command)
private void ResolutionFailed() => WriteEvent(ResolutionFailedEventId);

[NonEvent]
public static bool AnyDiagnosticsEnabled() => Log.IsEnabled() || NameResolutionMetrics.IsEnabled() || NameResolutionActivity.IsTracingEnabled();
public static bool AnyDiagnosticsEnabled() => !OperatingSystem.IsWasi() && (Log.IsEnabled() || NameResolutionMetrics.IsEnabled() || NameResolutionActivity.IsTracingEnabled());

[NonEvent]
public NameResolutionActivity BeforeResolution(object hostNameOrAddress, long startingTimestamp = 0)
Expand Down Expand Up @@ -91,6 +91,8 @@ public NameResolutionActivity BeforeResolution(object hostNameOrAddress, long st
[NonEvent]
public void AfterResolution(object hostNameOrAddress, in NameResolutionActivity activity, object? answer, Exception? exception = null)
{
if (OperatingSystem.IsWasi()) return;

if (!activity.Stop(answer, exception, out TimeSpan duration))
{
// We stopped the System.Diagnostics.Activity at this point and neither metrics nor EventSource is enabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ public async Task Dns_GetHostAddressesAsync_NullHost_Fail()
await Assert.ThrowsAsync<ArgumentNullException>(() => Dns.GetHostAddressesAsync(null));
}

[Fact]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))]
public void DnsBeginGetHostAddresses_BadName_Throws()
{
IAsyncResult asyncObject = Dns.BeginGetHostAddresses("BadName", null, null);
Assert.ThrowsAny<SocketException>(() => Dns.EndGetHostAddresses(asyncObject));
}

[Fact]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))]
public void DnsBeginGetHostAddresses_BadIpString_ReturnsAddress()
{
IAsyncResult asyncObject = Dns.BeginGetHostAddresses("0.0.1.1", null, null);
Expand All @@ -86,7 +86,7 @@ public void DnsBeginGetHostAddresses_BadIpString_ReturnsAddress()
Assert.Equal(IPAddress.Parse("0.0.1.1"), results[0]);
}

[Fact]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))]
public void DnsBeginGetHostAddresses_MachineName_MatchesGetHostAddresses()
{
IAsyncResult asyncObject = Dns.BeginGetHostAddresses(TestSettings.LocalHost, null, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace System.Net.NameResolution.Tests
{
[SkipOnPlatform(TestPlatforms.Wasi, "WASI has no getnameinfo")]
public class GetHostByAddressTest
{
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void DnsObsoleteBeginGetHostByName_BadName_Throws()
Assert.ThrowsAny<SocketException>(() => Dns.EndGetHostByName(asyncObject));
}

[Fact]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))]
public void DnsObsoleteBeginGetHostByName_IPv4String_ReturnsOnlyGivenIP()
{
IAsyncResult asyncObject = Dns.BeginGetHostByName(IPAddress.Loopback.ToString(), null, null);
Expand Down Expand Up @@ -106,6 +106,7 @@ public void DnsObsoleteGetHostByName_IPv6String_ReturnsOnlyGivenIP()
[ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/107339", TestPlatforms.Wasi)]
public void DnsObsoleteGetHostByName_EmptyString_ReturnsHostName()
{
IPHostEntry entry = Dns.GetHostByName("");
Expand Down
Loading
Loading