Use the Perl Net::DNS modules
As the naming service for the Internet, DNS is a crucial infrastructure in all enterprises, large and small. DNS has been around for more than 15 years and was first defined as part of Internet Engineering Task Force (IETF) Request for Comments (RFC) 1034 and RFC 1035 and later updated in RFC 2181. Microsoft finally moved from its proprietary WINS to DNS with the introduction of Active Directory (AD).
AD uses DNS as part of the service locator process. When a Windows-based client attempts to contact a domain controller (DC), Global Catalog (GC) server, or Kerberos Key Distribution Center (KDC), AD queries DNS in the background to find the most optimal server to use. If you understand how entries, or Resource Records (RRs), are structured in DNS and the basics of how to use the Perl Net::DNS modules, you can write scripts and applications to perform functions such as finding all the DCs in a site or domain, finding all the GC servers in a forest, and updating DNS when you bring new servers online.
RRs are DNS entries that correspond to a type of name lookup. The many different RR types correspond to various types of DNS lookups (go to http://www.microsoft.com/windows2000/en/server/help/sag_dns_add_rr-reference.htm for a list and descriptions of Windows 2000supported RR types). AD uses four record types most frequently: Host address (A), Pointer (PTR), Canonical name (CNAME), and Service locator (SRV).
You're probably most familiar with the A record, which maps a DNS domain name to an IP address. You use this record to perform a forward lookup—a DNS lookup of a name to find its IP address. Figure 1, page 2, shows an Nslookup command that performs an A record lookup on the server named dc1.xyz.com.
PTR records map an IP address to a name, which is known as a reverse lookup. You use the PTR record when you attempt to find the name for a particular IP address, as the example in Figure 2, page 2, shows.
CNAME records map an alias or alternative name for a server to its real name. The ability to use an alias often comes in handy, especially on Web servers. For example, if you want clients to go to intranet.xyz.com to access an internal Web site, but the Web server name is web1.xyz.com, a CNAME record of intranet.xyz.com that points to web1.xyz.com will do the trick. Figure 3, page 2, shows an Nslookup command that queries a CNAME record and returns the server name.
SRV records locate servers that provide a particular service for a client. As I described earlier, AD uses these records in its service locator process. Figure 4, page 2, shows how to query for SRV records. In this example, I query for SRV records matching _ldap._tcp.dc._msdcs.xyz.com to locate the DCs in the xyz.com domain.
Notice that SRV records provide additional information, including priority, weight, and port. Typically, all DCs' SRV records have a priority of 0 and a weight of 100, so that information isn't of use. The port number is the port associated with the service that the host provides. In Figure 4, the DC SRV record returns the Lightweight Directory Access Protocol (LDAP) port—port 389. GC SRV records return port 3268, and Kerberos SRV records return port 88.
Querying DNS with Net::DNS
Because DNS is ubiquitous and vital, especially to AD, you can benefit from understanding how to programmatically query it. Perl gives you a couple of different options. You can use the Perl built-in functions gethostbyname() and gethostbyaddr() to resolve names to IP addresses and vice versa. You can't use them to query SRV records and perform dynamic updates. They also don't provide as much control over the DNS query process as the Perl Net::DNS modules do.
The Net::DNS set of modules provides a flexible, object-oriented (OO) interface for querying and updating all types of DNS records. To obtain the latest release of Net::DNS, go to http://www.net-dns.org or http:// search.cpan.org/dist/Net-DNS. As I write this article, the latest version available from the Net::DNS and CPAN Web sites is 0.28. I recommend that you use this version or a later one because some older versions don't support the Windows platform very well.
One big benefit of Perl and Net::DNS is that they're cross-platform, which means that you can use them to write scripts on a variety of platforms to query and update DNS. Using Net::DNS to query DNS is straightforward. Listing 1 shows a sample script that queries for the _ldap._tcp.dc._msdcs.DomainName SRV record. As I described previously, each DC in the DomainName domain has the _ldap._tcp.dc._msdcs.Domain Name SRV record. Using Net::DNS to find the hosts with this SRV record is a simple way to obtain a list of all of a domain's DCs.
The first step in using Net::DNS is to create a new instance of the Net::DNS::Resolver object, as the line at callout A in Listing 1 shows. You can then invoke the nameservers() method, as the line at callout B shows, if you want to point your query or update at a specific name server. If you don't use the nameservers() method, the script uses the name servers configured on the local host, either statically or through DHCP. If you use a version of Net::DNS earlier than 0.20, you must use nameservers().
At callout C, the search() method queries for a specific RR. The first parameter is the name to search for. The second parameter is optional and is the RR type to search for. If you don't specify a second parameter, the query will match against any RR type.
The result of the search() is a Net::DNS::Packet object. The script uses the answer() method against this object to retrieve each matching RR, as the code in callout D shows. Each RR is in the form of a Net::DNS::RR object for the specific record type.
Each RR type has a module under Net::DNS::RR (i.e., Net::DNS::RR:: RecordType) that implements that RR type's methods. You can use these methods to retrieve the data available for that RR type. For example, you can use the Net::DNS::RR::SRV module's target() method to find the name of the host that matches the SRV record.
The script in Web Listing 1 (http://www.winscriptingsolutions.com, InstantDoc ID 26630), is similar to that in Listing 1 except that the query is for the _ldap._tcp.gc._msdcs.ForestName SRV record, which returns all GC servers in the ForestName forest. Because GC servers are forestwide, not specific to a domain, Web Listing 1 queries ForestName instead of querying DomainName as Listing 1 does. Another difference is that for each matching host, Web Listing 1 performs a second query—against the A record to determine the host's IP address.
In the early days of DNS, you typically added and deleted RRs by modifying zone files directly on a name server (as on a BIND server) or through a GUI tool (as on a Microsoft DNS server). A standard way for clients to dynamically update DNS information didn't exist. Then in 1997, RFC 2136 defined a dynamic DNS (DDNS) update process in which clients can send update messages to a DNS server. DDNS updates help you automate DNS information maintenance. Client machines can maintain their own DNS information—for example, A and PTR records—without DNS administrator intervention.
AD DCs also use DDNS to keep their SRV records up-to-date. In large AD environments, maintaining DC DNS information manually can be a lot of work and prone to error. For that reason, DCs send out DDNS updates for their RRs periodically to ensure their DNS information is current. Each DC maintains a netlogon.dns file in %systemroot%\system32\config that contains all its RRs.
If you understand how to use DDNS in a program to update DNS, you can write a simple command-line utility or batch program to perform a mass DNS update, or write a more complex Web-based application that dynamically queries and updates DNS in your environment. The sample script in Listing 2 can accept two parameters at the command line—a host name and IP address—then adds an A record and PTR record for that host.
The script in Listing 2 begins similarly to the previous listings by creating a Net::DNS::Resolver object. The next block of code creates the A record. First, the script creates a new Net::DNS::Update object by specifying the name of the zone that it's going to update—in this case, 'xyz.com.'. Then, the script pushes a new DDNS request onto the stack. After the push, the script uses the Net::DNS::Resolver object to send the update request. The script then checks for errors on the resulting Net::DNS::Packet object; if none exist, the script displays a message indicating that it successfully added the record.
The next block of code creates the PTR record. Note that depending on how your reverse zones are configured, you might need to customize the reverse zone that $rvs_zone specifies in the push() method. See your DNS administrator for more information about how reverse zones are set up in your environment.
To perform DDNS updates from a script, the name server must allow updates from the host on which you run the script. Another option is to use authenticated updates with Transaction Signature (TSIG). For more information about how to use TSIG with Net::DNS, type
at a command line.
DNS is a crucial service that everything from AD to the Internet relies on for name lookups. The Perl Net::DNS modules are a flexible, cross-platform interface for querying and updating DNS information. If you understand the RRs that AD uses, you can easily plug routines into your scripts and applications to find all DCs in a domain or site or find GC servers in a forest, among other things. You can also create scripts that use DDNS updates to dynamically maintain information in DNS. Understanding not only the information DNS provides but how to obtain and change that information programmatically can come in handy.