Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Modified Dns.GetHostAddressesAsync to be truly async (#26850)
Browse files Browse the repository at this point in the history
* Modified Dns.GetHostAddressesAsync to be truly async

* Applied code review recommendations

* Unix build fix

* Unix build fix #2

* Unix build fix #3

* NETFX build fix

* Fixed useGetHostByName logic

* Simplified ProcessResult code

* Cleaned up cancel code

* cleanup
  • Loading branch information
JeffCyr authored and stephentoub committed Feb 12, 2018
1 parent 14185df commit d3ff31e
Show file tree
Hide file tree
Showing 15 changed files with 523 additions and 88 deletions.
25 changes: 25 additions & 0 deletions src/Common/src/Interop/Windows/Winsock/AddressInfoEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Net.Internals;
using System.Runtime.InteropServices;

namespace System.Net.Sockets
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct AddressInfoEx
{
internal AddressInfoHints ai_flags;
internal AddressFamily ai_family;
internal SocketType ai_socktype;
internal ProtocolFamily ai_protocol;
internal int ai_addrlen;
internal IntPtr ai_canonname; // Ptr to the canonical name - check for NULL
internal byte* ai_addr; // Ptr to the sockaddr structure
internal IntPtr ai_blob; // Unused ptr to blob data about provider
internal int ai_bloblen;
internal IntPtr ai_provider; // Unused ptr to the namespace provider guid
internal AddressInfoEx* ai_next; // Next structure in linked list
}
}
36 changes: 36 additions & 0 deletions src/Common/src/Interop/Windows/Winsock/Interop.GetAddrInfoExW.cs
Original file line number Diff line number Diff line change
@@ -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.
// See the LICENSE file in the project root for more information.

using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;

internal static partial class Interop
{
internal static partial class Winsock
{
internal const string GetAddrInfoExCancelFunctionName = "GetAddrInfoExCancel";

internal unsafe delegate void LPLOOKUPSERVICE_COMPLETION_ROUTINE([In] int dwError, [In] int dwBytes, [In] NativeOverlapped* lpOverlapped);

[DllImport(Interop.Libraries.Ws2_32, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern unsafe int GetAddrInfoExW(
[In] string pName,
[In] string pServiceName,
[In] int dwNamespace,
[In] IntPtr lpNspId,
[In] ref AddressInfoEx pHints,
[Out] out AddressInfoEx* ppResult,
[In] IntPtr timeout,
[In] ref NativeOverlapped lpOverlapped,
[In] LPLOOKUPSERVICE_COMPLETION_ROUTINE lpCompletionRoutine,
[Out] out IntPtr lpNameHandle
);

[DllImport("ws2_32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern unsafe void FreeAddrInfoEx([In] AddressInfoEx* pAddrInfo);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal partial class Interop
internal partial class Kernel32
{
public const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
public const int LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800;

[DllImport(Libraries.Kernel32, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeLibraryHandle LoadLibraryExW([In] string lpwLibFileName, [In] IntPtr hFile, [In] uint dwFlags);
Expand Down
8 changes: 4 additions & 4 deletions src/Common/src/System/Net/SocketAddressPal.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ public static unsafe void SetPort(byte[] buffer, ushort port)
ThrowOnFailure(err);
}

public static unsafe uint GetIPv4Address(byte[] buffer)
public static unsafe uint GetIPv4Address(ReadOnlySpan<byte> buffer)
{
uint ipAddress;
Interop.Error err;
fixed (byte* rawAddress = buffer)
fixed (byte* rawAddress = &MemoryMarshal.GetReference(buffer))
{
err = Interop.Sys.GetIPv4Address(rawAddress, buffer.Length, &ipAddress);
}
Expand All @@ -115,11 +115,11 @@ public static unsafe uint GetIPv4Address(byte[] buffer)
return ipAddress;
}

public static unsafe void GetIPv6Address(byte[] buffer, Span<byte> address, out uint scope)
public static unsafe void GetIPv6Address(ReadOnlySpan<byte> buffer, Span<byte> address, out uint scope)
{
uint localScope;
Interop.Error err;
fixed (byte* rawAddress = buffer)
fixed (byte* rawAddress = &MemoryMarshal.GetReference(buffer))
fixed (byte* ipAddress = &MemoryMarshal.GetReference(address))
{
err = Interop.Sys.GetIPv6Address(rawAddress, buffer.Length, ipAddress, address.Length, &localScope);
Expand Down
4 changes: 2 additions & 2 deletions src/Common/src/System/Net/SocketAddressPal.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static unsafe void SetPort(byte[] buffer, ushort port)
port.HostToNetworkBytes(buffer, 2);
}

public static unsafe uint GetIPv4Address(byte[] buffer)
public static unsafe uint GetIPv4Address(ReadOnlySpan<byte> buffer)
{
unchecked
{
Expand All @@ -49,7 +49,7 @@ public static unsafe uint GetIPv4Address(byte[] buffer)
}
}

public static unsafe void GetIPv6Address(byte[] buffer, Span<byte> address, out uint scope)
public static unsafe void GetIPv6Address(ReadOnlySpan<byte> buffer, Span<byte> address, out uint scope)
{
for (int i = 0; i < address.Length; i++)
{
Expand Down
33 changes: 28 additions & 5 deletions src/System.Net.NameResolution/src/System.Net.NameResolution.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<AssemblyName>System.Net.NameResolution</AssemblyName>
<ProjectGuid>{1714448C-211E-48C1-8B7E-4EE667D336A1}</ProjectGuid>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='uap-Windows_NT-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='uap-Windows_NT-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Windows_NT-Debug|AnyCPU'" />
Expand Down Expand Up @@ -46,6 +46,10 @@
<Compile Include="$(CommonPath)\System\Net\IPEndPointStatics.cs">
<Link>Common\System\Net\IPEndPointStatics.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Net\ByteOrder.cs">
<Link>Common\System\Net\ByteOrder.cs</Link>
</Compile>
<Compile Include="System\Net\DnsResolveAsyncResult.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetsWindows)' == 'true'">
<Compile Include="System\Net\NameResolutionPal.Windows.cs" />
Expand All @@ -72,6 +76,9 @@
<Compile Include="$(CommonPath)\System\Net\SocketProtocolSupportPal.Windows.cs">
<Link>Common\System\Net\SocketProtocolSupportPal.Windows</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Net\SocketAddressPal.Windows.cs">
<Link>Common\System\Net\SocketAddressPal.Windows</Link>
</Compile>
<!-- Interop -->
<Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs">
<Link>Interop\Windows\Interop.Libraries.cs</Link>
Expand Down Expand Up @@ -118,12 +125,27 @@
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\SafeFreeAddrInfo.cs">
<Link>Interop\Windows\Winsock\SafeFreeAddrInfo.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\AddressInfoEx.cs">
<Link>Interop\Windows\Winsock\AddressInfoEx.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.GetAddrInfoExW.cs">
<Link>Interop\Windows\Winsock\Interop.GetAddrInfoExW.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GetProcAddress.cs">
<Link>Interop\Windows\Kernel32\Interop.GetProcAddress.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.LoadLibraryEx.cs">
<Link>Interop\Windows\Kernel32\Interop.LoadLibraryEx.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.FreeLibrary.cs">
<Link>Interop\Windows\Kernel32\Interop.FreeLibrary.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
<Compile Include="System\Net\NameResolutionPal.Unix.cs" />
<Compile Include="$(CommonPath)\System\Net\ByteOrder.cs">
<Link>Common\System\Net\Internals\ByteOrder.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Net\ContextAwareResult.Unix.cs">
<Link>Common\System\Net\ContextAwareResult.Unix.cs</Link>
</Compile>
Expand Down Expand Up @@ -189,7 +211,8 @@
<Reference Include="System.Security.Claims" />
<Reference Include="System.Security.Principal.Windows" />
<Reference Include="System.Threading" />
<Reference Include="System.Threading.Overlapped" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
</Project>
88 changes: 40 additions & 48 deletions src/System.Net.NameResolution/src/System/Net/DNS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,22 @@ public static IPHostEntry GetHostByName(string hostName)
return InternalGetHostByName(hostName, false);
}

private static IPHostEntry InternalGetHostByName(string hostName, bool includeIPv6)
private static void ValidateHostName(string hostName)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(null, hostName);
IPHostEntry ipHostEntry = null;

if (hostName.Length > MaxHostName // If 255 chars, the last one must be a dot.
|| hostName.Length == MaxHostName && hostName[MaxHostName - 1] != '.')
{
throw new ArgumentOutOfRangeException(nameof(hostName), SR.Format(SR.net_toolong,
nameof(hostName), MaxHostName.ToString(NumberFormatInfo.CurrentInfo)));
}
}

private static IPHostEntry InternalGetHostByName(string hostName, bool includeIPv6)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(null, hostName);
IPHostEntry ipHostEntry = null;

ValidateHostName(hostName);

//
// IPv6 Changes: IPv6 requires the use of getaddrinfo() rather
Expand Down Expand Up @@ -252,42 +257,19 @@ public static IPHostEntry Resolve(string hostName)
return ipHostEntry;
}

private class ResolveAsyncResult : ContextAwareResult
{
// Forward lookup
internal ResolveAsyncResult(string hostName, object myObject, bool includeIPv6, object myState, AsyncCallback myCallBack) :
base(myObject, myState, myCallBack)
{
this.hostName = hostName;
this.includeIPv6 = includeIPv6;
}

// Reverse lookup
internal ResolveAsyncResult(IPAddress address, object myObject, bool includeIPv6, object myState, AsyncCallback myCallBack) :
base(myObject, myState, myCallBack)
{
this.includeIPv6 = includeIPv6;
this.address = address;
}

internal readonly string hostName;
internal bool includeIPv6;
internal IPAddress address;
}

private static void ResolveCallback(object context)
{
ResolveAsyncResult result = (ResolveAsyncResult)context;
DnsResolveAsyncResult result = (DnsResolveAsyncResult)context;
IPHostEntry hostEntry;
try
{
if (result.address != null)
if (result.IpAddress != null)
{
hostEntry = InternalGetHostByAddress(result.address, result.includeIPv6);
hostEntry = InternalGetHostByAddress(result.IpAddress, result.IncludeIPv6);
}
else
{
hostEntry = InternalGetHostByName(result.hostName, result.includeIPv6);
hostEntry = InternalGetHostByName(result.HostName, result.IncludeIPv6);
}
}
catch (OutOfMemoryException)
Expand Down Expand Up @@ -315,20 +297,20 @@ private static IAsyncResult HostResolutionBeginHelper(string hostName, bool just
if (NetEventSource.IsEnabled) NetEventSource.Info(null, hostName);

// See if it's an IP Address.
IPAddress address;
ResolveAsyncResult asyncResult;
if (IPAddress.TryParse(hostName, out address))
IPAddress ipAddress;
DnsResolveAsyncResult asyncResult;
if (IPAddress.TryParse(hostName, out ipAddress))
{
if (throwOnIIPAny && (address.Equals(IPAddress.Any) || address.Equals(IPAddress.IPv6Any)))
if (throwOnIIPAny && (ipAddress.Equals(IPAddress.Any) || ipAddress.Equals(IPAddress.IPv6Any)))
{
throw new ArgumentException(SR.net_invalid_ip_addr, nameof(hostName));
}

asyncResult = new ResolveAsyncResult(address, null, includeIPv6, state, requestCallback);
asyncResult = new DnsResolveAsyncResult(ipAddress, null, includeIPv6, state, requestCallback);

if (justReturnParsedIp)
{
IPHostEntry hostEntry = NameResolutionUtilities.GetUnresolvedAnswer(address);
IPHostEntry hostEntry = NameResolutionUtilities.GetUnresolvedAnswer(ipAddress);
asyncResult.StartPostingAsyncOp(false);
asyncResult.InvokeCallback(hostEntry);
asyncResult.FinishPostingAsyncOp();
Expand All @@ -337,19 +319,29 @@ private static IAsyncResult HostResolutionBeginHelper(string hostName, bool just
}
else
{
asyncResult = new ResolveAsyncResult(hostName, null, includeIPv6, state, requestCallback);
asyncResult = new DnsResolveAsyncResult(hostName, null, includeIPv6, state, requestCallback);
}

// Set up the context, possibly flow.
asyncResult.StartPostingAsyncOp(false);

// Start the resolve.
Task.Factory.StartNew(
s => ResolveCallback(s),
asyncResult,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
// If the OS supports it and 'hostName' is not an IP Address, resolve the name asynchronously
// instead of calling the synchronous version in the ThreadPool.
if (NameResolutionPal.SupportsGetAddrInfoAsync && ipAddress == null)
{
ValidateHostName(hostName);
NameResolutionPal.GetAddrInfoAsync(asyncResult);
}
else
{
// Start the resolve.
Task.Factory.StartNew(
s => ResolveCallback(s),
asyncResult,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
}

// Finish the flowing, maybe it completed? This does nothing if we didn't initiate the flowing above.
asyncResult.FinishPostingAsyncOp();
Expand All @@ -371,7 +363,7 @@ private static IAsyncResult HostResolutionBeginHelper(IPAddress address, bool fl
if (NetEventSource.IsEnabled) NetEventSource.Info(null, address);

// Set up the context, possibly flow.
ResolveAsyncResult asyncResult = new ResolveAsyncResult(address, null, includeIPv6, state, requestCallback);
DnsResolveAsyncResult asyncResult = new DnsResolveAsyncResult(address, null, includeIPv6, state, requestCallback);
if (flowContext)
{
asyncResult.StartPostingAsyncOp(false);
Expand Down Expand Up @@ -399,7 +391,7 @@ private static IPHostEntry HostResolutionEndHelper(IAsyncResult asyncResult)
{
throw new ArgumentNullException(nameof(asyncResult));
}
ResolveAsyncResult castedResult = asyncResult as ResolveAsyncResult;
DnsResolveAsyncResult castedResult = asyncResult as DnsResolveAsyncResult;
if (castedResult == null)
{
throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
Expand Down Expand Up @@ -611,7 +603,7 @@ public static IPHostEntry EndResolve(IAsyncResult asyncResult)
}
catch (SocketException ex)
{
IPAddress address = ((ResolveAsyncResult)asyncResult).address;
IPAddress address = ((DnsResolveAsyncResult)asyncResult).IpAddress;
if (address == null)
throw; // BeginResolve was called with a HostName, not an IPAddress

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Net
{
internal sealed class DnsResolveAsyncResult : ContextAwareResult
{
internal string HostName { get; }
internal bool IncludeIPv6 { get; }
internal IPAddress IpAddress { get; }

// Forward lookup
internal DnsResolveAsyncResult(string hostName, object myObject, bool includeIPv6, object myState, AsyncCallback myCallBack)
: base(myObject, myState, myCallBack)
{
HostName = hostName;
IncludeIPv6 = includeIPv6;
}

// Reverse lookup
internal DnsResolveAsyncResult(IPAddress ipAddress, object myObject, bool includeIPv6, object myState, AsyncCallback myCallBack)
: base(myObject, myState, myCallBack)
{
IncludeIPv6 = includeIPv6;
IpAddress = ipAddress;
}
}
}
Loading

0 comments on commit d3ff31e

Please sign in to comment.