Как сокет Perl разрешает имена хостов в Linux?

У меня есть (насколько я могу судить) прекрасно работающая установка Linux (Ubuntu 8.04), где все инструменты (nslookup, curl, wget, firefox и т. д.) могут разрешать адреса. Тем не менее, следующий код не работает:

$s = new IO::Socket::INET(
    PeerAddr => 'stackoverflow.com',
    PeerPort => 80,
    Proto => 'tcp',
);

die "Error: $!\n" unless $s;

Я проверил следующие вещи:

  • Perl может разрешать адреса с помощью gethostbyname (т.е. приведенный ниже код работает):

    my $ret = gethostbyname('stackoverflow.com'); print inet_ntoa($ret);

  • Оригинальный исходный код работает под Windows

  • Вот как это должно работать (т.е. оно должно разрешать имена хостов), поскольку LWP пытается использовать это поведение (на самом деле я наткнулся на проблему, пытаясь отладить, почему LWP не работает для меня)
  • Запуск скрипта не отправляет DNS-запросы (поэтому он даже не пытается разрешить имя). Проверено с помощью Wireshark

person Grey Panther    schedule 02.10.2008    source источник


Ответы (3)


На первый взгляд следующий код из IO::Socket::INET

sub _get_addr {
    my($sock,$addr_str, $multi) = @_;
    my @addr;
    if ($multi && $addr_str !~ /^\d+(?:\.\d+){3}$/) {
        (undef, undef, undef, undef, @addr) = gethostbyname($addr_str);
    } else {
        my $h = inet_aton($addr_str);
        push(@addr, $h) if defined $h;
    }
    @addr;
}

предлагает (если вы посмотрите на вызывающую сторону этого кода) обходной путь добавления MultiHomed => 1, к вашему коду.

Без этого обходного пути приведенный выше код пытается вызвать inet_aton("hostname.com") с помощью inet_aton() из Socket.pm. Это работает для меня как в Win32, так и в Unix, так что я думаю, что для вас это поломка.

См. Socket.xs для исходного кода inet_aton:

void
inet_aton(host)
    char *  host
    CODE:
    {
        struct in_addr ip_address;
        struct hostent * phe;

        if (phe = gethostbyname(host)) {
            Copy( phe->h_addr, &ip_address, phe->h_length, char );
        } else {
            ip_address.s_addr = inet_addr(host);
        }

        ST(0) = sv_newmortal();
        if(ip_address.s_addr != INADDR_NONE) {
            sv_setpvn( ST(0), (char *)&ip_address, sizeof ip_address );
        }
    }

Похоже, что Perl gethostbyname() работает для вас лучше, чем C gethostbyname().

person tye    schedule 02.10.2008
comment
Да! Да! ДА! :-) Это была проблема. Большое спасибо за решение. Теперь все, что мне нужно выяснить, это как убедить LWP создать специальный socked (с активированной опцией multihomed). - person Grey Panther; 02.10.2008

Не могли бы вы рассказать нам, почему ваш код дает сбой? У вас есть код проверки ошибок, но вы не сообщили, что это за ошибка!

Я только что попробовал исходный код (с добавлением «use IO::Socket::INET» на моем компьютере с Mac OS X, и он отлично работает.

Я подозреваю, что опция Multihomed является ненужным взломом, и какая-то другая проблема является основной причиной вашей проблемы.

person Alnitak    schedule 06.10.2008

Убедитесь, что у вас есть заявление

use IO::Socket::INET;

В начале вашего исходного кода. Если вы пропустите это, вы, вероятно, получите сообщение об ошибке:

Не удается найти метод объекта "новый" через пакет "IO::Socket::INET"

Кроме того, вы можете проверить, работает ли DNS, используя Net::DNS::Resoler, см. дополнительную информацию здесь.

use Net::DNS;

my $res = Net::DNS::Resolver->new;

# Perform a lookup, using the searchlist if appropriate.
my $answer = $res->search('example.com');
person paxos1977    schedule 02.10.2008
comment
У меня есть оператор использования в начале скрипта. Кроме того, я попробовал второй тестовый скрипт, он работает (как и следовало ожидать, учитывая, что gethostbyname работает), но у меня все еще есть исходная проблема. - person Grey Panther; 02.10.2008