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

System.Net.Dns does not always resolve "" and the system's own hostname on Unix #36849

Open
antonfirsov opened this issue May 21, 2020 · 7 comments
Labels
area-System.Net enhancement Product code improvement that does NOT require public API changes/additions
Milestone

Comments

@antonfirsov
Copy link
Member

antonfirsov commented May 21, 2020

A follow-up on the discussion in #36072.

Problem

dotnet/corefx#41764 implemented a consistent solution to match Windows behavior when Dns.GetHostEntry(hostName) or related methods are invoked with "" or the systems hostname on Unix, but it only works on Unix systems which are configured to successfully resolve their own hostname. Although most of the time this is true, there are some exceptions, eg. our 10.14 CI macs don't do it. I'm not sure about the root cause on mac, but it's relatively easy to "misconfigure" Ubuntu 18.04, see last paragraph. On these systems the shell command hostname | nslookup (and the matching libc call chain) fails, which makes our current System.Net.Dns implementation throw. This does not conform our documentation:

If an empty string is passed as the hostNameOrAddress argument, then this method returns the IPv4 and IPv6 addresses of the local host.

Suggestion

In pal_networking.c we are already handling hostname as a special case:

result = gethostname((char*)name, _POSIX_HOST_NAME_MAX);
bool includeIPv4Loopback = true;
bool includeIPv6Loopback = true;
if (result == 0 && strcasecmp((const char*)address, name) == 0)
{
// Get all interface addresses if the host name corresponds to the local host.
result = getifaddrs(&addrs);

We should return the same result, even if the underlying OS name resolution (getaddrinfo) fails.

Despite it's name, the System.Net.Dns class is a universal name resolver that goes behind the DNS resolution rules, implementing a .NET-specific logic for name resolution. It's better to aim for cross-platform consistency, instead of matching the behavior of nslookup / getaddrinfo (which is not being matched anyways since dotnet/corefx#41764)

Note: on Windows and "properly configured" Unixes, Dns.GetHostEntry("") and Dns.GetHostEntry("my-hostname") are essentially the same, we should not change this behavior IMO.

CC @wfurt @scalablecory @davidsh

Reproduction on Ubuntu 18.04

In /etc/nsswitch.conf:

- hosts:          files mdns4_minimal [NOTFOUND=return] dns myhostname
+ hosts:          files dns

In /etc/resolv.conf:

- nameserver 127.0.0.53
+ nameserver 8.8.8.8

In /etc/hosts:

- 127.0.1.1 <hostname>
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.Net untriaged New issue has not been triaged by the area owner labels May 21, 2020
@ghost
Copy link

ghost commented May 21, 2020

Tagging subscribers to this area: @dotnet/ncl
Notify danmosemsft if you want to be subscribed.

@antonfirsov antonfirsov removed the untriaged New issue has not been triaged by the area owner label May 21, 2020
@antonfirsov antonfirsov self-assigned this May 21, 2020
@scalablecory
Copy link
Contributor

This seems like very corner-case thing, to the point where if someone configured their system in such a way I might assume it was intentional.

We have Dns.GetHostEntry("") documented as returning the current system's IPs, but I don't know if we should extend it to also work if you happen to give the system's hostname as a string too.

@wfurt
Copy link
Member

wfurt commented May 22, 2020

We actually already do in SystemNative_GetHostEntryForName()

result = gethostname((char*)name, _POSIX_HOST_NAME_MAX);
bool includeIPv4Loopback = true;
bool includeIPv6Loopback = true;
if (result == 0 && strcasecmp((const char*)address, name) == 0)
{
// Get all interface addresses if the host name corresponds to the local host.

And it actually may work that way on some Linuxes with systems.

https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html

The problem is that if getaddrinfo() fails, we never get to that part of the code.

@antonfirsov
Copy link
Member Author

This seems like very corner-case thing, to the point where if someone configured their system in such a way I might assume it was intentional.

This was never the case with the issues we encountered our CI, and it's more likely that we get user complaints for not being able to resolve hostname or "" than for the opposite. As of @wfurt's last comment, to me this doesn't look like a radical change in our existing behavior, it's more about making it more consistent and robust.

@antonfirsov
Copy link
Member Author

An open question:
Since we won't be able to return ai_canonname when gethostname fails, we need to decide what to return in IPHostEntry.HostName. It can be either null or or just mirror the input hostName.

@wfurt
Copy link
Member

wfurt commented Jun 12, 2020

I would suggest to return hostName it self to prevent issues with null.

@karelz karelz added this to the Future milestone Jul 30, 2020
@karelz karelz added the enhancement Product code improvement that does NOT require public API changes/additions label Jul 30, 2020
@mjsabby
Copy link
Contributor

mjsabby commented Aug 15, 2020

So does .NET have its own resolution for dns to ip or does it use the glibc/musl API? We should actually follow what the Go community did and parse resolv.conf and not use glibc/musl at all.

Reason: kubernetes/kubernetes#62628

The K8s world loves to add proxies and sidecars for many things, and they use iptables a lot. Combined with iptables and IPv6 being enabled, glibc and musl do parallel DNS requests which expose a race condition in the netfilter kernel module. This causes timeouts which are 5-seconds by default.

In glibc you can configure that the request should be done in serial order, but apparently that is not supported in musl.

So, if this issue is we should implement our own resolv.conf parser (I recall seeing some code in the repo for that?) and use that then it would solve this other problem too and make .NET resilient to this dns timeout issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Net enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

No branches or pull requests

6 participants