为什么 localhost 解析为 ::1 而不是 127.0.0.1

为什么 localhost 解析为 ::1 而不是 127.0.0.1

有了getent hosts localhost,我才得到::1,虽然我期待127.0.0.1。我已经禁用了 IPv6,所以获得::1更令人惊讶。更令人困惑的是,当我 时ping localhost,ping 被发送到127.0.0.1哪个作品。有人可以解释一下吗?

~: 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

编辑:localhost我的/etc/hosts.

答案1

找到这个并不容易(但很有趣:))。

简短回答

gethostbyname2() 使用 __lookup_name(),具有一些环回('lo')接口的硬编码值。当您为“getent ports”命令指定“localhost”时,它最终会在尝试 IPv4 之前使用 IPv6 的默认值,因此最终会得到 ::1。您可以更改 getent 的代码以获得 127.0.0.1,如下所示:

  1. 下载getent源码来自 github
  2. 在getent.c 下的hosts_keys() 中注释掉以下行(#329): //else if ((host = gethostbyname2 (key[i], AF_INET6)) == NULL)
  3. 从源代码编译并运行:

结果:

$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

更多细节

getent 工具使用由穆斯林图书馆。当我们运行命令时

$getent hosts localhost

该工具调用getent.c 下的hosts_keys() 函数来解析提供的密钥。该函数尝试通过 4 种方法进行解析:

  1. IPv6 的 gethostbyaddr(在此实例中失败)。
  2. IPv4 的 gethostbyaddr(在此实例中失败)。
  3. IPv6 的 gethostbyname2(由于硬编码值,对于 localhost 总是成功)。
  4. IPv4 的 gethostbyname2(由于在#3 上成功,所以不尝试)。

所有musl功能都在/src/network/下实现,看这里。 gethostbyname2()(在 gethostbyname2.c 中实现)调用 gethostbyname2_r()(在 gethostbyname2_r.c 中实现),后者调用 __lookup_name()(在lookup_name.c 中)。 __lookup_name() 再次作为如何解析主机名的几个选项,第一个是 name_from_null (在同一文件中):

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;
}

最后,我们可以看到,当 family == AF_INET6 时,我们将得到硬编码值::1。由于 getent 在 IPv4 之前尝试 IPv6,因此这将是返回值。正如我上面所示,在 getent 中强制解析为 IPv4 将导致上面函数中的硬编码 127.0.0.1 值。

如果您希望更改功能以返回本地主机的 IPv4 地址,最好的办法是首先提交/请求修复 getent 以搜索 IPv4。

希望这可以帮助!

相关内容