localhost が 127.0.0.1 ではなく ::1 と確認されるのはなぜですか?

localhost が 127.0.0.1 ではなく ::1 と確認されるのはなぜですか?

それで私はそれを期待したにもかかわらず、getent hosts localhostちょうどそれを得ました。 IPv6を無効にしたので、もっと驚きです。もっと混乱しているのは、私がいつpingを送っているのかということです。誰かがこれを説明できますか?::1127.0.0.1::1ping localhost127.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() は、ループバック ('lo') インターフェイスにいくつかのハードコーディングされた値とともに __lookup_name() を使用します。 "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の下のhost_key()関数を呼び出します。この関数は、次の4つの方法で解析を試みます。

  1. IPv6用のgethostbyaddr(このインスタンスでは失敗)
  2. IPv4用のgethostbyaddr(この場合は失敗)
  3. IPv6用のgethostbyname2(ハードコードされた値が原因でlocalhostに対して常に成功)
  4. IPv4用のgethostbyname2(#3で成功した後に試行されません)

すべてのmusl機能は/src/network/の下に実装されています。ねえ。 gethostbyname2() (gethostbyname2.c で実装されている) は __lookup_name() (lookup_name.c で) を呼び出す gethostbyname2_r() (gethostbyname2_r.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を取得することが最善の方法です。

役に立ったことを願っています!

関連情報