DekGenius.com
I l@ve RuBoard Previous Section Next Section

15.3 Perl Programming with Net::DNS

If using the shell to parse nslookup's output seems too awkward and writing a C program seems too complicated, consider writing your program in Perl using the Net::DNS module written by Michael Fuhr. You'll find the package at http://www.perl.com/CPAN-local/modules/by-module/Net/Net-DNS-0.12.tar.gz.

Net::DNS treats resolvers, DNS messages, sections of DNS messages, and individual resource records as objects and provides methods for setting or querying each object's attributes. We'll examine each object type first, then give a Perl version of our check_soa program.

15.3.1 Resolver Objects

Before making any queries, you must first create a resolver object:

$res = new Net::DNS::Resolver;

Resolver objects are initialized from your resolv.conf file, but you can change the default settings by making calls to the object's methods. Many of the methods described in the Net::DNS::Resolver manual page correspond to fields and options in the _res structure described earlier in this chapter. For example, if you want to set the number of times the resolver tries each query before timing out, you can call the $res->retry method:

$res->retry(2);

To make a query, call one of the following methods:

$res->search
$res->query
$res->send

These methods behave like the res_search, res_query, and res_send library functions described in the C programming section, though they take fewer arguments. You must provide a domain name, and you can optionally provide a record type and class (the default behavior is to query for A records in the IN class). These methods return Net::DNS::Packet objects, which we'll describe next. Here are a few examples:

$packet = $res->search("terminator");
$packet = $res->query("movie.edu", "MX");
$packet = $res->send("version.bind", "TXT", "CH");

15.3.2 Packet Objects

Resolver queries return Net::DNS::Packet objects, whose methods you can use to access the header, question, answer, authority, and additional sections of a DNS message:

$header     = $packet->header;
@question   = $packet->question;
@answer     = $packet->answer;
@authority  = $packet->authority;
@additional = $packet->additional;

15.3.3 Header Objects

DNS message headers are returned as Net::DNS::Header objects. The methods described in the Net::DNS::Header manual page correspond to the header fields described in RFC 1035 and in the HEADER structure used in C programs. For example, if you want to find out if this is an authoritative answer, you would call the $header->aa method:

if ($header->aa) {
    print "answer is authoritative\n";
} else {
    print "answer is not authoritative\n";
}

15.3.4 Question Objects

The question section of a DNS message is returned as a list of Net::DNS::Question objects. You can find the name, type, and class of a question object with the following methods:

$question->qname
$question->qtype
$question->qclass

15.3.5 Resource Record Objects

The answer, authority, and additional sections of a DNS message are returned as lists of Net::DNS::RR objects. You can find the name, type, class, and TTL of an RR object with the following methods:

$rr->name
$rr->type
$rr->class
$rr->ttl

Each record type is a subclass of Net::DNS::RR and has its own type-specific methods. Here's an example that shows how to get the preference and mail exchanger out of an MX record:

$preference = $rr->preference;
$exchanger  = $rr->exchange;

15.3.6 A Perl Version of check_soa

Now that we've described the objects Net::DNS uses, let's look at how to use them in a complete program. We've rewritten check_soa in Perl:

#!/usr/local/bin/perl -w

use Net::DNS;

#----------------------------------------------------------------------
# Get the zone from the command line.
#----------------------------------------------------------------------

die "Usage:  check_soa zone\n" unless @ARGV == 1;
$domain = $ARGV[0];

#----------------------------------------------------------------------
# Find all the name servers for the zone.
#----------------------------------------------------------------------

$res = new Net::DNS::Resolver;

$res->defnames(0);
$res->retry(2);

$ns_req = $res->query($domain, "NS");
die "No name servers found for $domain: ", $res->errorstring, "\n"
    unless defined($ns_req) and ($ns_req->header->ancount > 0);

@nameservers = grep { $_->type eq "NS" } $ns_req->answer;

#----------------------------------------------------------------------
# Check the SOA record on each name server.
#----------------------------------------------------------------------

$| = 1;
$res->recurse(0);

foreach $nsrr (@nameservers) {

  #------------------------------------------------------------------
  # Set the resolver to query this name server.
  #------------------------------------------------------------------

  $ns = $nsrr->nsdname;
  print "$ns ";

  unless ($res->nameservers($ns)) {
      warn ": can't find address: ", $res->errorstring, "\n";
      next;
  }

  #------------------------------------------------------------------
  # Get the SOA record.
  #------------------------------------------------------------------

  $soa_req = $res->send($domain, "SOA");
  unless (defined($soa_req)) {
      warn ": ", $res->errorstring, "\n";
      next;
  }

  #------------------------------------------------------------------
  # Is this name server authoritative for the zone?
  #------------------------------------------------------------------

  unless ($soa_req->header->aa) {
      warn "is not authoritative for $domain\n";
      next;
  }

  #------------------------------------------------------------------
  # We should have received exactly one answer.
  #------------------------------------------------------------------

  unless ($soa_req->header->ancount == 1) {
      warn ": expected 1 answer, got ",
            $soa_req->header->ancount, "\n";
      next;
  }

  #------------------------------------------------------------------
  # Did we receive an SOA record?
  #------------------------------------------------------------------

   unless (($soa_req->answer)[0]->type eq "SOA") {
       warn ": expected SOA, got ",
            ($soa_req->answer)[0]->type, "\n";
       next;
  }

  #------------------------------------------------------------------
  # Print the serial number.
  #------------------------------------------------------------------

  print "has serial number ", ($soa_req->answer)[0]->serial, "\n";
}

Now that you've seen how to write a DNS program using a shell script, a Perl script, and C code, you should be able to write one on your own using the language that best fits your situation.

    I l@ve RuBoard Previous Section Next Section