MavericksからYosemiteにアップグレードしたところ、curl
にループバックホスト名が表示されなくなりました。
テストする単純なhttpサーバーを設定します。
$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
これで、Chromeでlocalhost:8000を打つことができます。私もそれを得ることができます。しかし、カールではこれが起こります:
$ curl localhost:8000
curl: (7) Failed to connect to localhost port 8000: Connection refused
ただし、これは機能します:
$ curl 127.0.0.1:8000
私は wgetプロキシ設定に関するこの回答 を読みましたが、これは機能するため、役に立ちませんでした:
$ wget --proxy=off localhost:8000
/etc/hosts
ファイルにいくつかの異なるループバックホスト名がリストされているため、ローカルでアプリを開発でき、curlでのデバッグに慣れているため、これは本当にイライラします。
私はosxに同梱されているバージョンのcurlを試しました:
$ curl --version
curl 7.37.1 (x86_64-Apple-darwin14.0) libcurl/7.37.1 SecureTransport zlib/1.2.5
Protocols: dict file ftp ftps Gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IPv6 Largefile NTLM NTLM_WB SSL libz
$ curl localhost:8000
curl: (7) Failed to connect to localhost port 8000: Connection refused
$ curl 127.0.0.1 # works
そして、私はbrewでカールをコンパイルしてみました:
$ /usr/local/Cellar/curl/7.38.0/bin/curl --version
curl 7.38.0 (x86_64-Apple-darwin14.0.0) libcurl/7.38.0 SecureTransport zlib/1.2.5
Protocols: dict file ftp ftps Gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smtp smtps telnet tftp
Features: IPv6 Largefile NTLM NTLM_WB SSL libz
$ /usr/local/Cellar/curl/7.38.0/bin/curl localhost:8000
curl: (7) Failed to connect to localhost port 8000: Connection refused
$ /usr/local/Cellar/curl/7.38.0/bin/curl 127.0.0.1:8000 # works
/ etc/hostsファイルからIPv6ループバック行の1つをコメントアウトすることで機能します。
#fe80::1%lo0 localhost
これで、localhostだけでなく、すべてのループバックホスト名が機能します。これどうしたの?
代替(Sudoや/etc/hosts
の変更は必要ありません)-curlが賢くなるまで、常にipv4を使用します。
$ echo '--ipv4' >> ~/.curlrc
(その後、すべてが期待どおりに動作します)
まず、0.0.0.0
は「任意のIPv4アドレス」を意味する特別なアドレスです。
ソケットはIPv4またはIPv6プロトコルのいずれかにバインドできます。ソケットが0.0.0.0
にバインドされている場合は、ソケットが接続しようとするすべてのIPv4を待機し、次のように表されます。
$ nc -l 0.0.0.0 8085
$ lsof -i4 -Pnl | grep 8085
nc 23994 [xxx] 3u IPv4 [xxx] 0t0 TCP *:8085 (LISTEN)
*
記号は、IPv4の0.0.0.0
と同等です。
IPv6の場合:
$ nc -l :: 8085
$ lsof -i6 -Pnl | grep 8085
nc 24145 [xxx] 3u IPv6 [xxx] 0t0 TCP *:8085 (LISTEN)
*
記号は、IPv6の::
に相当します 公式仕様のように 。
その理由は、curl
が/etc/hosts
のランダムなlocalhost
エントリに解決しようとし、@ NickRetallackが言及したように、そのエントリは、curl
がデフォルトモードで解決するときにlocalhost
によって選択されたものです(IPv6またはIPv4、最初に解決されるものは何でも)。
@_CharlesHebdoughが示唆するように、--ipv4
モードで強制すると、curl
はlocalhost
を127.0.0.1
に解決します(/etc/hosts
にlocalhost
の他のIPv4エントリがないと仮定します)。
各実装は、必要に応じてlocalhost
を解決するため、さまざまなツールで断続的に成功したのはなぜですか。
できるだけ正確にするには、localhostの代わりに127.0.0.1
を使用しますが、IPv4にバインドされます。 localhost
を使用すると、IPv6とIPv4の両方のプロトコルで柔軟に機能しますが、実装によっては、特定のバージョンのcurl
のように問題が発生する場合があります。