"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 messages is quite straightforward -- you don't have to deal with ASN.1[103] at all, as you do with SNMP. And you have nifty library routines to make parsing DNS messages 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 as we go through this chapter; at least have a copy of it nearby when you write your own DNS programs.
[103]ASN.1 stands for Abstract Syntax Notation. ASN.1 is a method of encoding object types, accepted as an international standard by the International Organization for Standardization.
Each of these problems is easily detectable. If a name server is not running on a host, the host sends back an ICMP port unreachable message. You can find this out with either a query tool or the resolver routines. Checking whether a name server is authoritative for a zone is easy: ask it for the zone's SOA record. If the answer is nonauthoritative or the name server does not have the SOA record, there's 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.
Let's write the basic program as a shell script that uses nslookup. First, we 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 name servers are supposed to be authoritative for the zone, both when the server is authoritative for the zone that contains the NS records and when it isn't:
Find out what the response looks like when the name server is not authoritative for the NS records:% nslookup Default Server: relay.hp.com Address: 15.255.152.2 > set type=ns
Then find out what the response looks like when the name server is 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 = W20NS.MIT.EDU mit.edu nameserver = BITSY.MIT.EDU 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
You can see from this output that we can grab the domain names of the name servers by looking for the lines that contain nameserver and saving the last field. When the name server wasn't authoritative for the NS records, it printed them twice, so we'll have to weed out duplicates.> 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 = W20NS.MIT.EDU BITSY.MIT.EDU internet address = 18.72.0.3 STRAWB.MIT.EDU internet address = 18.71.0.151 W20NS.MIT.EDU internet address = 18.70.0.160
Next, we look up the SOA record for the zone, both when the server is authoritative for the zone that contains 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:
Find out what the response looks like when the name server is not authoritative and does not have the SOA record:% nslookup Default Server: relay.hp.com Address: 15.255.152.2 > set type=soa > set norecurse
Then find out what the response looks like when the name server is authoritative for the zone:> 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
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 that it was nonauthoritative. 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.> 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 = 1995 refresh = 3600 (1H) retry = 900 (15M) expire = 3600000 (5w6d16h) minimum ttl = 21600 (6H)
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:
Last, we need to see what nslookup returns if a host is not responding. We can test this by switching name servers to an unused IP address on our LAN:% 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
In the last two cases, the error message was written to stderr.[104] 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:% 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
[104]Not all versions of nslookup print the last error message for a timeout. Be sure to check what yours prints.
Here is what the output looks like:#!/bin/sh if test "$1" = "" then echo usage: $0 zone exit 1 fi ZONE=$1 # # Use nslookup to discover the name servers for this zone ($1). # Use awk to grab the name server's domain 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 $ZONE |\ 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 temp 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 domain 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 $ZONE 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 $ZONE 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 domain 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.$$
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 in this chapter.% check_soa mit.edu BITSY.MIT.EDU has serial number 1995 STRAWB.MIT.EDU has serial number 1995 W20NS.MIT.EDU has serial number 1995