Fopen() URL-адресов для доменных имен, а не для числовых адресов

После нескольких попыток отладки стороннего приложения, имеющего проблемы с fopen(), я наконец обнаружил, что

php -r 'echo(file_get_contents("http://www.google.com/robots.txt"));'

не удается, но

php -r 'echo(file_get_contents("http://173.194.32.81/robots.txt"));'

преуспевает. Обратите внимание, что в качестве пользователя веб-сервера я могу выполнить ping на www.google.com, и он отлично справляется.

Я прервал оба исполнения PHP, и они расходятся так:

Для числового URL-адреса v4:

socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("173.194
poll([{fd=3, events=POLLOUT}], 1, 0)    = 0 (Timeout)
...[bunch of poll/select/recvfrom]...
close(3)                                = 0

Для имени домена:

socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
close(3)                                 = 0

PHP даже не пытался что-либо сделать с этим сокетом. Или даже разрешите домен, если на то пошло. WTF?

Повторная компиляция PHP с поддержкой ipv6 или без него, похоже, не имеет значения. Отключение ipv6 в этой системе нежелательно.

Gentoo Linux, PHP 5.3.14, в настоящее время пытается попробовать PHP 5.4 и посмотреть, помогает ли это. У кого-нибудь есть идея?

EDIT:

php -r 'echo gethostbyname("www.google.com");'

Работает и дает ipv4, а

php -r 'echo(file_get_contents("http://[2a00:1450:4007:803::1011]/"));'

Кажется, возвращает пустой результат.

ИЗМЕНИТЬ 2:

Я даже не заметил в первый раз, что гнездо v6, открытое при использовании имени, является SOCK_DGRAM. Этот PHP пытается решить доменное имя? Я попытался переключить свой резольвер с 127.0.0.1 на: 1 в resolv.conf, и это не помогло.

Ответ 1

GDB показал, что этот таинственный неиспользуемый вызов сокета действительно пришел из libcurl. Я перекомпилировал php без libcurl, и он работает. Я продолжу расследование причины, но пока что обходной путь работает.

Ответ 2

Я думаю, что это предпочтение операционной системы, а не предпочтение php. Я думаю, вам нужно отредактировать файл /etc/gai.conf, чтобы предпочесть IPv4 через IPv6. Немного о Googling появилась в этой статье, которая описывает, как это сделать. Честно говоря, я не знаком с gai.conf, поэтому ваш пробег может отличаться, но похоже, что в большинстве систем он будет просто раскомментировать одну строку.

Что касается того, почему вы видите SOCK_DGRAM, если я должен был догадаться, я бы предположил, что DNS-запросы - это UDP, вероятно, то, что вы видите в трассировке, тогда как в вашей первой трассе адрес, скорее всего, был кэширован поэтому он немедленно установил TCP-соединение (DOCK_STREAM) на удаленный сервер.