PerlでDNS逆引き

研究室仲間から数百のIPアドレスのリストをDNSサーバからホスト名を逆引きしてまとめて変換出来ないか聞かれたので調べてみた。PerlではCPANで配布されているNet::DNSモジュールを使うのが一般的のようだ。使い方が以下のサイトに紹介されていた。

上記URLのDNSの逆引きコードを以下のように関数化してスクリプトに組み込んで使ってみた。しかし動いていることは動いているのだが…、逆引き処理がとても遅くて使い物にならない。timeコマンドで調べたところ下記の処理だけで4.2秒も掛かっているではないか! CPANの本家のサイトを見たら、BUGの項目に"Net::DNS is slow."と書いてあったのでNet::DNSを使う限り処理速度の問題は解決出来ないようだ。

#!/usr/bin/perl

use strict;
use warnings;
use Net::DNS;

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

sub resolve_dns {
    my ($res_ref, $ip) = @_;

    my $resolved_name = "none";
    my $query = $$res_ref->query($ip);
    
    if ($query) {
	foreach my $rr ($query->answer) {
	    next if $rr->type ne "PTR";
	    $resolved_name = $rr->ptrdname;
	}
    } else {
	print "Error: ", $$res_ref->errorstring, "\n";
    }
 
    return $resolved_name;
}

my $name = resolve_dns(\$res, $ip);
print "name= $name\n";

しかし手打ちでnslookupでDNSの逆引きしたら4秒も掛からずすぐに逆引き出来る。だったらNet::DNSを使わずに、nslookupをそのまま使えばいいのでは?ということでPIPE経由でnslookupを呼び出し、DNSの逆引きの出力からホスト名だけ切り出す関数を以下のように書いた。こちらだとComcast + Cygwin環境だったら0.8秒しか掛からなかった。更に大学のイントラネット + Fedora14の環境だと0.065秒まで短縮された。Perlでの名前の逆引きはこちらの方が個人的にはおすすめ。

#!/usr/bin/perl

use strict;
use warnings;

my $ip = "192.0.34.166";

sub resolve_dns {
    my ($ip) = @_;
    my $resolved_name = "none";
    my $pipe;

    open($pipe, "nslookup $ip |");

    while(<$pipe>){
	#if(/name = (.+)/) {  # For Linux (confirmed on Fedora14)
	if(/Name:\s+(.+)/) { # For cygwin
	    $resolved_name = $1;
	}
    }

    close($pipe);
    return $resolved_name;
}

my $name = resolve_dns($ip);
print "name= $name\n";

The above is a code to resolve hostname from IP address via DNS transaction. As far as I checked Net::DNS is too slow! (It took 4.2secs). I recommend to call nslookup and extract the hostname from the output via pipe interface.