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

`Dns.GetHostEntryAsync` returning odd results #405

Daniel15 opened this issue Nov 30, 2019 · 4 comments


Copy link

@Daniel15 Daniel15 commented Nov 30, 2019

Dns.GetHostEntryAsync is returning weird results for me. Calling it on several IPs such as, and are returning my server's domain name as the HostName value, rather than the proper reverse DNS of the IP address. The host command on the server does return the correct values though:

% host domain name pointer

% host domain name pointer

% host domain name pointer

How can I debug this? How does the DNS resolution in the framework work?


This comment has been minimized.

Copy link

@Daniel15 Daniel15 commented Dec 1, 2019

So NameResolutionPal.TryGetNameInfo returns the right hostname, but Dns.GetHostEntryAsync doesn't. My guess is that it's something to do with this logic that does a forward lookup right after performing the reverse lookup:

// Do a reverse lookup to get the host name.
string name = NameResolutionPal.TryGetNameInfo(address, out SocketError errorCode, out int nativeErrorCode);
if (errorCode != SocketError.Success)
if (NetEventSource.IsEnabled) NetEventSource.Error(address, $"{address} DNS lookup failed with {errorCode}");
throw SocketExceptionFactory.CreateSocketException(errorCode, nativeErrorCode);
// Do the forward lookup to get the IPs for that host name
errorCode = NameResolutionPal.TryGetAddrInfo(name, justAddresses, out string hostName, out string[] aliases, out IPAddress[] addresses, out nativeErrorCode);

Is there some way to avoid this? I just want the PTR record, I don't care about if the forward lookup matches.

NameResolutionPal.TryGetNameInfo is perfect for my use case, but unfortunately it's internal-only 😕 I was testing by copying and pasting the code into my app, but obviously that's not really maintainable.


This comment has been minimized.

Copy link

@Daniel15 Daniel15 commented Dec 1, 2019

Here's my example code where I copied and pasted the NameResolutionPal internals to directly test TryGetNameInfo:

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

namespace DnsTest
	class Program
		static async Task Main(string[] args)
			await Task.WhenAll(

		static async Task Lookup(string ip)
			var result = await Dns.GetHostEntryAsync(ip);
			Console.WriteLine($"{ip} Dns.GetHostEntryAsync = {result.HostName}");

			var result2 = TryGetNameInfo(IPAddress.Parse(ip), out var socketError, out var nativeErrorCode);
			Console.WriteLine($"{ip} TryGetNameInfo = {result2}");

		public static unsafe string TryGetNameInfo(IPAddress addr, out SocketError socketError, out int nativeErrorCode)
			byte* buffer = stackalloc byte[Interop.Sys.NI_MAXHOST + 1 /*for null*/];

			byte isIPv6;
			int rawAddressLength;
			if (addr.AddressFamily == AddressFamily.InterNetwork)
				isIPv6 = 0;
				rawAddressLength = IPAddressParserStatics.IPv4AddressBytes;
				isIPv6 = 1;
				rawAddressLength = IPAddressParserStatics.IPv6AddressBytes;

			byte* rawAddress = stackalloc byte[rawAddressLength];
			addr.TryWriteBytes(new Span<byte>(rawAddress, rawAddressLength), out int bytesWritten);
			Debug.Assert(bytesWritten == rawAddressLength);

			int error = Interop.Sys.GetNameInfo(

			socketError = GetSocketErrorForNativeError(error);
			nativeErrorCode = error;
			return socketError == SocketError.Success ? Marshal.PtrToStringAnsi((IntPtr)buffer) : null;

		private static SocketError GetSocketErrorForNativeError(int error)
			switch (error)
				case 0:
					return SocketError.Success;
				case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_AGAIN:
					return SocketError.TryAgain;
				case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_BADFLAGS:
				case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_BADARG:
					return SocketError.InvalidArgument;
				case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_FAIL:
					return SocketError.NoRecovery;
				case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_FAMILY:
					return SocketError.AddressFamilyNotSupported;
				case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_NONAME:
					return SocketError.HostNotFound;
				case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_MEMORY:
					throw new OutOfMemoryException();
					Debug.Fail("Unexpected error: " + error.ToString());
					return SocketError.SocketError;

	internal static partial class Interop
		internal static partial class Sys
			internal const int NI_MAXHOST = 1025;

			internal enum GetNameInfoFlags : int
				NI_NAMEREQD = 0x1,

			internal enum GetAddrInfoErrorFlags : int
				EAI_AGAIN = 1,      // Temporary failure in name resolution.
				EAI_BADFLAGS = 2,   // Invalid value for `ai_flags' field.
				EAI_FAIL = 3,       // Non-recoverable failure in name resolution.
				EAI_FAMILY = 4,     // 'ai_family' not supported.
				EAI_NONAME = 5,     // NAME or SERVICE is unknown.
				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.

			[DllImport("System.Native", EntryPoint = "SystemNative_GetNameInfo")] // Libraries.SystemNative
			internal static extern unsafe int GetNameInfo(
				byte* address,
				uint addressLength,
				byte isIpv6,
				byte* host,
				uint hostLength,
				byte* service,
				uint serviceLength,
				GetNameInfoFlags flags);

	internal static class IPAddressParserStatics
		public const int IPv4AddressBytes = 4;
		public const int IPv6AddressBytes = 16;
		public const int IPv6AddressShorts = IPv6AddressBytes / 2;

Output when I run it:

% ./DnsTest Dns.GetHostEntryAsync = TryGetNameInfo = Dns.GetHostEntryAsync = TryGetNameInfo = Dns.GetHostEntryAsync = TryGetNameInfo =

So GetHostEntryAsync is definitely acting weird here. I might just use DnsClient.NET instead of the framework DNS code since it seems more reliable for this use case.

@scalablecory scalablecory added bug os-linux and removed untriaged labels Dec 2, 2019
@scalablecory scalablecory added this to the 5.0 milestone Dec 2, 2019

This comment has been minimized.

Copy link

@scalablecory scalablecory commented Dec 2, 2019

The Dns class is a bit of a misnomer. It is not just DNS, but rather a name resolver -- it can look at more things than just DNS (e.g. /etc/hosts, NetBIOS names, etc.). So the result might not be incorrect here -- is it returning just your local domain name and stopping there, or also returning DNS-based domains?


This comment has been minimized.

Copy link

@Daniel15 Daniel15 commented Dec 3, 2019

Turns out the issue is that I have a wildcard subdomain (so [anything] returns a valid IP), which was causing this behaviour. ping actually exhibited the same behaviour:

% ping asdfasdfasdfasdf
PING ( 56(84) bytes of data.
64 bytes from ( icmp_seq=1 ttl=64 time=0.063 ms

I found this interesting section in the resolv.conf manpage (

   search Search list for host-name lookup.
          By default, the search list contains one entry, the local
          domain name.  It is determined from the local hostname
          returned by gethostname(2); the local domain name is taken to
          be everything after the first '.'.  Finally, if the hostname
          does not contain a '.', the root domain is assumed as the
          local domain name.

So what the Linux DNS resolver was doing was concatenating to the end of the requested hostname.


search .

to /etc/resolv.conf fixed it, as it prevents the behaviour.

@Daniel15 Daniel15 closed this Dec 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet
3 participants
You can’t perform that action at this time.