#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->{список поиска}»?