start page | rating of books | rating of authors | reviews | copyrights

DNS & BIND

DNS & BINDSearch this book
Previous: 13.6 Problem Symptoms Chapter 14 Next: 14.2 C Programming with the Resolver Library Routines
 

14. Programming with the Resolver and Name Server Library Routines

Contents:
Shell Script Programming with nslookup
C Programming with the Resolver Library Routines
Perl Programming with Net::DNS

"I know what you're thinking about," said Tweedledum; "but it isn't so, nohow."

"Contrariwise," continued Tweedledee, "if it was so, it might be; and if it were so, it would be; but as it isn't, it ain't. That's logic."

I bet you think resolver programming is hard. Contrariwise! It isn't very hard, really. The format of DNS packets is quite straightforward - you don't have to deal with ASN .1[1] at all, as you have to do with SNMP . And you have nifty library routines to make parsing DNS packets easy. We've included portions of RFC 1035 in Appendix A, DNS Message Format and Resource Records . However, you might find it handy to have a copy of RFC 1035 to look at while we are going through this chapter; at least have a copy of it nearby when you write your own DNS program.

[1] ASN .1 stands for Abstract Syntax Notation. ASN .1 is a language for encoding object types, accepted as an international standard by the International Organization for Standardization.

14.1 Shell Script Programming with nslookup

Before you go off and write a C program to do your DNS chore, you should write the program as a shell script using nslookup . There are good reasons to start with a shell script:

If you prefer Perl over plain old shell programming, you can do that too. At the end of this chapter, we'll show you how to use the Perl Net:: DNS module written by Michael Fuhr.

14.1.1 A Typical Problem

Before you write a program, you have to have a problem to solve. Let's suppose you want your network management system to watch over your primary and secondary name servers. You want it to notify you of several problems: a name server that is not running (it might have died), a name server that is not authoritative for a domain that it is supposed to be authoritative for (the boot file might have been messed up), or a name server that has fallen behind in updating its data (the primary's serial number might have been decreased accidentally).

Each of these problems is easily detectable. If a name server is not running on a host, the host will send back an ICMP port unreachable message. You can find this out with both nslookup and the resolver routines. Checking if a name server is authoritative for a domain is easy: ask it for the domain's SOA record. If the answer is nonauthoritative, or the name server does not have the SOA record, there is a problem. You'll have to ask for the SOA record in a nonrecursive query so that the name server doesn't go off and look up the SOA record from another server. Once you have the SOA record, you can extract the serial number.

14.1.2 Solving This Problem with a Script

This problem requires a program that takes the domain name of a zone as an argument, looks up the name servers for that zone, and then queries each of those name servers for the SOA record for the zone. The response will show if the server is authoritative, and it will show the zone's serial number. If there is no response, the program needs to determine if a name server is even running on the host. Once this program is written, it needs to be called for every zone you want to watch over. Since this program looks up the name servers (by looking up the NS records for the zone), we assume that you have listed all your name servers in NS records in your zone data. If that is not the case, then you will have to change this program to take a list of name servers from the command line.

Let's write the basic program as a shell script that uses nslookup . First, we must figure out what the output of nslookup looks like, so that we can parse it with UNIX tools. We'll look up NS records to find out which servers are supposed to be authoritative for a zone, both when the server is authoritative for the NS records and when it isn't:

% 

nslookup

 Default Server:  relay.hp.com Address:  15.255.152.2  > 

set type=ns

Find out what the response looks like when the server is not authoritative for the NS records:

> 

mit.edu.

 Server:  relay.hp.com Address:  15.255.152.2  Non-authoritative answer: mit.edu nameserver = 
STRAWB
.
MIT
.
EDU
 mit.edu nameserver = W20
NS
.
MIT
.
EDU
 mit.edu nameserver = 
BITSY
.
MIT
.
EDU
  Authoritative answers can be found from: 
MIT
.
EDU
 nameserver = 
STRAWB
.
MIT
.
EDU
 
MIT
.
EDU
 nameserver = W20
NS
.
MIT
.
EDU
 
MIT
.
EDU
 nameserver = 
BITSY
.
MIT
.
EDU
 
STRAWB
.
MIT
.
EDU
  internet address = 18.71.0.151 W20
NS
.
MIT
.
EDU
   internet address = 18.70.0.160 BITSY.MIT.EDU   internet address = 18.72.0.3

Find out what the response looks like when the server is authoritative for the NS records:

> 

server strawb.mit.edu.

 Default Server:  strawb.mit.edu Address:  18.71.0.151  > 

mit.edu.

 Server:  strawb.mit.edu Address:  18.71.0.151  mit.edu nameserver = 
BITSY
.
MIT
.
EDU
 mit.edu nameserver = 
STRAWB
.
MIT
.
EDU
 mit.edu nameserver = W20
NS
.
MIT
.
EDU
 
BITSY
.
MIT
.
EDU
   internet address = 18.72.0.3 
STRAWB
.
MIT
.
EDU
  internet address = 18.71.0.151 W20
NS
.
MIT
.
EDU
   internet address = 18.70.0.160

You can see from this output that we can grab the name server names by looking for the lines that contain nameserver and saving the last field. When the server was not authoritative for the NS records, it printed them twice, so we'll have to weed out duplicates.

Next, we look up the SOA record for the zone, both when the server is authoritative for the SOA record and when it isn't. We turn off recurse so the name server doesn't go off and query an authoritative name server for the SOA :

% 

nslookup

 Default Server:  relay.hp.com Address:  15.255.152.2  > 

set type=soa

 > 

set norecurse

Find out what the response looks like when the server does not have the SOA record:

> 

mit.edu.

 Server:  relay.hp.com Address:  15.255.152.2  Authoritative answers can be found from: MIT.EDU nameserver = STRAWB.MIT.EDU MIT.EDU nameserver = W20NS.MIT.EDU MIT.EDU nameserver = BITSY.MIT.EDU STRAWB.MIT.EDU  internet address = 18.71.0.151 W20NS.MIT.EDU   internet address = 18.70.0.160 BITSY.MIT.EDU   internet address = 18.72.0.3

Find out what the response looks like when the server is authoritative for the zone:

> 

server strawb.mit.edu.

 Default Server:  strawb.mit.edu Address:  18.71.0.151  > 

mit.edu.

 Server:  strawb.mit.edu Address:  18.71.0.151  mit.edu         origin = 
BITSY
.
MIT
.
EDU
         mail addr = 
NETWORK
-
REQUEST
.
BITSY
.
MIT
.
EDU
         serial = 378         refresh = 3600 (1 hour)         retry   = 300 (5 mins)         expire  = 3600000 (41 days 16 hours)         minimum ttl = 21600 (6 hours)

When the name server was not authoritative for the zone, it returned references to other name servers. If the name server had previously looked up the SOA record and cached it, the name server would have returned the SOA record and said it was "non-authoritative." We need to check for both cases. When the name server returns the SOA record and it is authoritative, we can grab the serial number from the line that contains serial .

Now, we need to see what nslookup returns when no name server is running on a host. We'll change servers to a host that does not normally run a name server and look up an SOA record:

% 

nslookup

 Default Server:  relay.hp.com Address:  15.255.152.2  > 

server galt.cs.purdue.edu.

 Default Server:  galt.cs.purdue.edu Address:  128.10.2.39  > 

set type=soa

 > 

mit.edu.

 Server:  galt.cs.purdue.edu Address:  128.10.2.39  *** galt.cs.purdue.edu can't find mit.edu.: No response from server

Last, we need to see what nslookup returns if a host is not responding. We can test this by switching servers to an unused IP address on our LAN :

% 

nslookup

 Default Server:  relay.hp.com Address:  15.255.152.2  > 

server 15.255.152.100

 Default Server:  [15.255.152.100] Address:  15.255.152.100  > 

set type=soa

 > 

mit.edu.

 Server:  [15.255.152.100] Address:  15.255.152.100  *** Request to [15.255.152.100] timed-out

In the last two error cases, the error message was written to stderr . We can make use of that fact when writing our shell script. Now we are ready to compose the shell script. We'll call it check_soa :

#!/bin/sh if test "$1" = "" then     echo usage: $0 domain     exit 1 fi 
DOMAIN
=$1 # # Use nslookup to discover the nameservers for this domain ($1). # Use awk to grab the name server names from the nameserver lines. # (The names are always in the last field.)  Use sort -u to weed out # duplicates; we don't actually care about collation. # 
SERVERS
=`nslookup -type=ns $
DOMAIN
 |\                  awk '/nameserver/ {print $
NF
}' | sort -u` if test "$
SERVERS
" = "" then     #     # Didn't find any servers.  Just quit silently; nslookup will     # have detected this error and printed a message.  That will     # suffice.     #     exit 1 fi # # Check each server's 
SOA
 serial number.  The output from # nslookup is saved in two tmp files: nso.$$ (standard output) # and nse.$$ (standard error).  These files are rewritten on # every iteration.  Turn off defname and search since we # should be dealing with fully qualified names. # # 
NOTE
: this loop is rather long; don't be fooled. # for i in $
SERVERS
 do   nslookup >/tmp/nso.$$ 2>/tmp/nse.$$ <<-
EOF
     server $i     set nosearch     set nodefname     set norecurse     set q=soa     $
DOMAIN
 
EOF
   #   # Does this response indicate that the current server ($i) is   # authoritative?  The server is 
NOT
 authoritative if (a) the   # response says so, or (b) the response tells you to find   # authoritative info elsewhere.   #   if egrep "Non-authoritative|Authoritative answers can be" \                                           /tmp/nso.$$ >/dev/null   then     echo $i is not authoritative for $
DOMAIN
     continue   fi   #   # We know the server is authoritative; extract the serial number.   #   
SERIAL
=`cat /tmp/nso.$$ | grep serial | sed -e "s/.*= //"`   if test "$
SERIAL
" = ""   then     #     # We get here if 
SERIAL
 is null.  In this case, there should     # be an error message from nslookup; so cat the "standard     # error" file.     #     cat /tmp/nse.$$   else     #     # Report the server's name and its serial number.     #     echo $i has serial number $
SERIAL
   fi done  # end of the "for" loop # # Delete the temporary files. # rm -f /tmp/nso.$$ /tmp/nse.$$

Here is what the output looks like:

% 

check_soa mit.edu

 
BITSY
.
MIT
.
EDU
 has serial number 378 
STRAWB
.
MIT
.
EDU
 has serial number 378 W20
NS
.
MIT
.
EDU
 has serial number 378

If you are pressed for time, this short tool will solve your problem, and you can go on to other work. If you find that you are checking lots of zones and that this tool is too slow, you'll want to convert it to a C program. Also, if you want more control over the error messages - rather than relying on nslookup for error messages - then you'll have to write a C program. We'll do just that, later on in this chapter.


Previous: 13.6 Problem Symptoms DNS & BIND Next: 14.2 C Programming with the Resolver Library Routines
13.6 Problem Symptoms Book Index 14.2 C Programming with the Resolver Library Routines