Robidu has asked for the wisdom of the Perl Monks concerning the following question:

Greetings! I'm using the following code snippet to determine the DNS name associated with a particular IP address (Net::DNS::Resolver, Net::DNS::Packet and Net::DNS::RR are imported by the script):
my $l_dns = Net::DNS::Resolver->new( nameservers => qw(127.0.0.1) ); my $l_response = $l_dns->query($l_ip); if($l_dns->errorstring eq 'NOERROR') { my $l_record; foreach $l_record ($l_response->answer()) { if($l_record->type() eq 'PTR') { $l_dns_name = $l_record->name(); last; } } } else { $l_dns_name = $l_ip; }
Here $l_dns_name and $l_ip are defined elsewhere in the script and contain the (just determined) DNS name and the original IP address.
The only problem: It merely returns the IP address in .in-addr-arpa format instead of a domain name associated with it.

What could be going wrong here?

And to answer the obligatory question in advance: No, gethostbyname et al. is not what I want - I intend to directly access the local name server for name resolution.

Any help is greatly appreciated!

Replies are listed 'Best First'.
Re: Net::DNS::Resolver doesn't return the desired domain name
by cavac (Prior) on Jun 11, 2015 at 16:30 UTC

    First of all, after re-formatting, adding the usual use strict; use warnings; and adding the missing use Net::DNS::Resolver and adding the missing variables, i get Net::DNS::Resolver->new(): nameservers must be an arrayref.

    Let's fix that and add some actual nameservers and an IP to query:

    #!/usr/bin/env perl use strict; use warnings; use Net::DNS::Resolver; my $l_ip = '216.92.34.251'; # one of the perlmonks.org servers my @nameservers = qw[8.8.8.8 8.8.4.4]; # Googles DNS Servers my $l_dns_name = $l_ip; my $l_dns = Net::DNS::Resolver->new( nameservers => \@nameservers ); my $l_response = $l_dns->query($l_ip); if($l_dns->errorstring eq 'NOERROR') { my $l_record; foreach $l_record ($l_response->answer()) { print 'Got: ', $l_record->type(), ' -> ', $l_record->name(), + "\n"; if($l_record->type() eq 'PTR') { $l_dns_name = $l_record->name(); last; } } } else { print $l_dns->errorstring, "\n"; }

    This gives us the result Got: PTR  ->  251.34.92.216.in-addr.arpa

    Took me a while playing with the code and reading the docs to find out the problem. Turns out that you have to use type-specific methods to parse the part of the answer you actually want. The complete answer from the nameserver is something like this record:

    251.34.92.216.in-addr.arpa. 6883 IN PTR perlmonks.org.

    name() returns the first part, the name ("key") of that record. But what you want is the second part (the "value"), for which you need type-specific query functions, in this case ptrdname() from the Net::DNS::RR::PTR module.

    So, here's what i finally ended up with:

    #!/usr/bin/env perl use strict; use warnings; use Net::DNS::Resolver; my $destip = '216.92.34.251'; # one of the perlmonks.org servers my @nameservers = qw[8.8.8.8 8.8.4.4]; # Googles DNS Servers my $ptr = getHostname($destip, \@nameservers, 'PTR'); print "Got name $ptr\n"; sub getHostname { my ($l_ip, $nameservers, $recordtype) = @_; my $l_dns_name = $l_ip; my $l_dns = Net::DNS::Resolver->new( nameservers => \@nameservers, + $recordtype ); my $l_response = $l_dns->query($l_ip); if(!defined($l_response)) { print "Error: Got no response\n"; return $l_ip; } if($l_dns->errorstring eq 'NOERROR') { my $l_record; foreach $l_record ($l_response->answer()) { print 'Got: ', $l_record->type(), ' -> ', $l_record->nam +e(), "\n"; if($l_record->type() eq 'PTR') { $l_dns_name = $l_record->ptrdname(); #$l_dns_name = $l_record->address(); last; } } } else { print $l_dns->errorstring, "\n"; } return $l_dns_name; }

    Which results in:
    Got: PTR -> 251.34.92.216.in-addr.arpa Got name perlmonks.org

    Be aware: Not every IP will resolve to a hostname. You will need to add appropriate error handling as well as handling Nameserver connection and lookup errors of all kinds.

    "For me, programming in Perl is like my cooking. The result may not always taste nice, but it's quick, painless and it get's food on the table."
      Ah, thanks! So ptrdname is the function that I've been hunting for, then.

      With things fixed up this way everything is now working as it is supposed to be. Thanks!

        For thinks like these, often times a good debugger/IDE can save you hours of frustration without having to add print. (Personally i use Komodo IDE, but probably there are a few other out there that are also very good).

        If you don't want to spend money or have some other reasons not to use an IDE, you can do
        print 'Record is of class: ', ref($l_record), "\n";
        which will result in
        Record is of class: Net::DNS::RR::PTR

        "For me, programming in Perl is like my cooking. The result may not always taste nice, but it's quick, painless and it get's food on the table."