Networking

為什麼 localhost 解析為 ::1 而不是 127.0.0.1

  • March 30, 2020

有了getent hosts localhost,我才得到::1,雖然我期待127.0.0.1。我禁用了 IPv6,所以得到::1更令人驚訝。更令人困惑的是,當 I 時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.

找到這並不容易(但很有趣:))。

簡短的回答

使用 __lookup_name() 的 gethostbyname2() 具有一些用於 loopback (’lo’) 介面的硬編碼值。當您為“getent hosts”命令指定“localhost”時,它最終會在嘗試 IPv4 之前使用 IPv6 的預設值,因此您最終會得到 ::1。您可以更改 getent 的程式碼以獲得 127.0.0.1 ,如下所示:

  1. 從github下載getent原始碼
  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 工具使用由musl 庫定義和實現的函式。當我們執行命令時

$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 值。

如果您希望更改功能以返回 localhost 的 IPv4 地址,最好的辦法是送出/請求修復,以便 getent 先搜尋 IPv4。

希望這可以幫助!

引用自:https://unix.stackexchange.com/questions/530976