7

With getent hosts localhost, I only get ::1, although I expect 127.0.0.1. I have IPv6 disabled, so getting ::1 is even more surprising. To add to the confusion, when I ping localhost, pings are sent to 127.0.0.1 which works. Can someone explain this?

~: getent hosts localhost
::1             localhost

~: grep 'hosts:' /etc/nsswitch.conf 
hosts: files mymachines myhostname resolve [!UNAVAIL=return] dns

~: cat /etc/sysctl.d/disable_ipv6.conf 
net.ipv6.conf.all.disable_ipv6=1

~: ping ::1
connect: Network is unreachable

~: ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.022 ms

~: ping localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.015 ms

edit: There is no localhost in my /etc/hosts.

  • Good question! man getent states: ... When one or more key arguments are provided, pass each key to gethostbyaddr(3) or gethostby‐name2(3), depending on whether a call to inet_pton(3) indicates that the key is an IPv6 or IPv4 address or not, and display the result. I'm curious what calls and file access goes on. Could you post a strace? – Edward Jul 19 '19 at 9:19
0

Finding this wasn't easy (but fun :)).

Short answer

gethostbyname2(), which uses __lookup_name(), has some hard-coded values for the loopback ('lo') interface. When you specify 'localhost' to the 'getent hosts' command it ends up using the default value for IPv6 before it tries IPv4, thus you end up with ::1. You can change the code of getent in order to get 127.0.0.1 like so:

  1. Download getent source code from github
  2. Comment-out the following line (#329) in hosts_keys() under getent.c: //else if ((host = gethostbyname2 (key[i], AF_INET6)) == NULL)
  3. Compile and run from source:

Result:

$make clean && make && ./getent hosts localhost
rm -f *.o
rm -f getent
gcc -g -Wall -std=gnu99 -w -c getent.c -o getent.o
gcc  getent.o -Wall -lm -o getent
127.0.0.1       localhost

More details

getent tool uses functions defined and implemented by the musl library. When we run the command

$getent hosts localhost

The tool calls the hosts_keys() function under getent.c in order to resolve the provided key. The function tries resolving by 4 methods:

  1. gethostbyaddr for IPv6 (fails in this instance).
  2. gethostbyaddr for IPv4 (fails in this instance).
  3. gethostbyname2 for IPv6 (succeeds always for localhost due to hard-coded values).
  4. gethostbyname2 for IPv4 (doesn't try due to success on #3).

All musl functions are implemented under /src/network/, see here. gethostbyname2() (implemented in gethostbyname2.c) calls gethostbyname2_r() (implemented in gethostbyname2_r.c), which calls __lookup_name() (in lookup_name.c). __lookup_name(), again, as a few options of how to resolve the host name, the first one being name_from_null (in the same file):

static int name_from_null(struct address buf[static 2], const char *name, int family, int flags)
{
    int cnt = 0;
    if (name) return 0;
    if (flags & AI_PASSIVE) {
            if (family != AF_INET6)
                    buf[cnt++] = (struct address){ .family = AF_INET };
            if (family != AF_INET)
                    buf[cnt++] = (struct address){ .family = AF_INET6 };
    } else {
            if (family != AF_INET6)
                    buf[cnt++] = (struct address){ .family = AF_INET, .addr = { 127,0,0,1 } };
            if (family != AF_INET)
                    buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } };
    }
    return cnt;
}

At the very end, we can see that when family == AF_INET6 we will get the hard-coded value of ::1. Since getent tries IPv6 before IPv4, this would be the returned value. As I showed above, forcing resolve as IPv4 in getent will result in the hard coded 127.0.0.1 value from the function above.

If you wish to change the functionality to return IPv4 address for localhost, best thing would be to submit/request a fix for getent to search for IPv4 first.

Hope this helps!

|improve this answer|||||
  • With a reasonably modern version of the getent command, you can also use getent ahostsv4 or getent ahostsv6 instead of getent hosts, to specify whether you want a IPv4 or IPv6-based result. – telcoM Mar 30 at 21:58
  • True, but the question was why this specific command results in the way it did. There are ways to work around it for sure. – Tgilgul Mar 31 at 5:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.