Skip to content

Commit

Permalink
[WASI] System.Net.NameResolution (#107351)
Browse files Browse the repository at this point in the history
Co-authored-by: Alexander Köplinger <[email protected]>
  • Loading branch information
pavelsavara and akoeplinger committed Sep 12, 2024
1 parent 122eb75 commit a833cfb
Show file tree
Hide file tree
Showing 28 changed files with 352 additions and 504 deletions.
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";
}

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

0 comments on commit a833cfb

Please sign in to comment.