Получение всех имен хостов с IP-адреса в Perl

#perl

#perl

Вопрос:

Я пытаюсь найти способ получить все имена хостов, которые преобразуются в IP-адрес.

Функция gethostbyaddr, по-видимому, извлекает только первую запись из DNS (независимо от того, находится ли она в скалярном контексте или в контексте списка).

Пример:

 my $hostname = gethostbyaddr(inet_aton($ip_to_check), AF_INET);
$print($hostname); //output: joe.example.com

my @hostnames = gethostbyaddr(inet_aton($ip_to_check), AF_INET);
foreach my $hostname (@hostnames){
 print "(", join(',',@hostnames), ")"; //output: (joe.example.com,,2,4,?)
}
  

С терминала:

 $ host 192.168.1.5
5.1.168.192.in-addr.arpa domain name pointer joe.example.com.
5.1.168.192.in-addr.arpa domain name pointer john.example.com.
  

Я слышал, что Net:: DNS немного более надежен, но мне не повезло, что он также извлекает все записи.

Ответ №1:

Я использовал комбинацию ответов, приведенных здесь и в других разделах stack overflow, чтобы найти ответ, который я искал.

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

# change IP from 192.168.1.15 to 15.1.168.192.in-addr.arpa for searching
my $target_IP = join('.', reverse split(/./, $ip_to_check)).".in-addr.arpa";

# query DNS
my $query = $res->query("$target_IP", "PTR");

# if a result is found
if ($query){
    print("Resolves to:n");

    # for every result, print the IP address
    foreach my $rr ($query->answer){
        # show all unless the type is PTR (pointer to a canonical name)
        next unless $rr->type eq "PTR";

        # remove the period at the end
        printf(substr($rr->rdatastr, 0, -1));
    }
}
  

Ответ №2:

gethostby... Интерфейс довольно старый и неуклюжий, он был определен еще в первобытные времена, прежде чем Perl получил ссылки и претензии на OO. И это работает не так, как вы пытаетесь его использовать. При использовании в контексте списка он возвращает основное имя в качестве первого элемента и разделенный пробелом (!) список псевдонимов в качестве второго:

 my ($hostname, $aliases) = gethostbyaddr($addr, AF_INET);
my @hostname = ($hostname, split ' ', $aliases);
say join ' ', @hostname;
  

Теперь это теория; Я не нашел никаких IP-адресов с несколькими записями PTR навскидку, поэтому я не могу проверить, gethostbyaddr действительно ли они вернутся — это, вероятно, зависит и от вашей базовой среды выполнения C — но это работает, если вы используете, например, gethostbyname с CNAME общим именем.

Комментарии:

1. Спасибо за помощь, но я не смог заставить это возвращать более одного доменного имени. Я предполагаю, что это потому, что функция gethostbyaddr просто устарела и не предназначена для использования, которое я искал.

2. Я быстро погуглил версию исходного кода для синтаксического анализа ответов DNS, который используют старые библиотеки C — и оказывается, что при поиске IP-адреса он просто останавливается на первой PTR записи ресурса. Возможно, это просто работает так, как задумано, поскольку не рекомендуется иметь несколько PTR записей. В свое время существовало понятие «канонического» имени хоста, на которое указывала бы PTR запись. Но другая причина, по которой этого не следует делать, заключается именно в том, что библиотека C выбирает одно из них случайным образом, давая непредсказуемые результаты. Так устроен мир, вам решать, нравится ли вам это.

Ответ №3:

Вот небольшая программа, которую я использую для поиска во всех записях PTR маски сети (например, 192.0.2.0 / 28) при выполнении задач отслеживания злоупотреблений. Он отправляет до 15 запросов в секунду, и когда все они отправляются, начинается чтение ответов (так что для правильной работы с большими сетевыми блоками потребуется небольшая работа).

 #!/usr/bin/env perl
use strict;
use warnings;

use Net::Netmask;
use Net::DNS;

@ARGV or die "$0 ip/cidrn";

my $block = Net::Netmask->new(shift);

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

my %sockets;

my $i = 0;
for my $i (1 .. $block->size - 1) {
    my $ip = $block->nth($i);

    my $reverse_ip = join ".", reverse split m/./, $ip;
    $reverse_ip .= ".in-addr.arpa";

    #print "$ipn";

    my $bgsock = $res->bgsend($reverse_ip, 'PTR');
    $sockets{$ip} = $bgsock;

    sleep 1 unless $i % 15;
}

$i = 0;
for my $i (1 .. $block->size - 1) {

    my $ip = $block->nth($i);

    my $socket = $sockets{$ip};
    my $wait   = 0;
    until ($res->bgisready($socket)) {
        print "waiting for $ipn" if $wait > 0;
        sleep 1   $wait;
        $wait  ;
    }
    my $packet = $res->bgread($socket);
    my @rr     = $packet->answer;

    printf "%-15s %sn", $ip, $res->errorstring
      unless @rr;

    for my $rr (@rr) {
        printf "%-15s %sn", $ip, $rr->string;
    }
}
  

Комментарии:

1. Спасибо за вашу помощь, я использовал части вашего скрипта в ответе на вопрос.

Ответ №4:

Я не думаю, что это правильно сформулированная постановка задачи. В общем случае существует почти бесконечное количество DNS-имен, которые могут преобразовываться в любой IP-адрес, даже неизвестный стороне, которой принадлежит адрес. Обратный поиск принципиально ненадежен и не способен ответить на вопрос, который хотел бы задать пользователь, поскольку все имена для IP не обязательно должны быть в видимой обратной карте.

Первый ответ, в котором перечисляется обратная карта, является лучшим, который можно сделать, но в нем будут пропущены любые имена, которые не были введены в карту.

Ответ №5:

Это то, что я использовал:

 sub getauthoritivename
    {
    my ($printerdns)=@_;
    my $res = Net::DNS::Resolver->new(searchlist=>$config->{searchlist});

    my $query = $res->search($printerdns);
    if ($query) 
        {
        foreach my $rr ($query->answer) 
            {
            next unless $rr->type eq "A";
            print $rr->name;
            }
        } 
    else
        {
        warn "query failed: ", $res->errorstring, "n";
        return 0;
        }
    }
  

Пока $ rr-> name находит имена, он продолжает их добавлять.

Комментарии:

1. Я не уверен, что понимаю. Откуда берется «searchlist=> $ config->{список поиска}»?