NAME

Net::DNS::ZoneCheck -- validate a DNS zone composed of RR records

SYNOPSIS

use Net::DNS::ZoneCheck;

Net::DNS::ZoneCheck::check(\@rrs, $zone);
Net::DNS::ZoneCheck::check(records => \@rrs, zone => $zone, %other_parameters);
my ($changed_records, $added_records, $deleted_records) =
  Net::DNS::ZoneCheck::compare(\@rrs1, \@rrs2);

DESCRIPTION

The module assumes that it deals with well-formed Net::DNS::RR records. That is, there are no checks to ensure that the records themselves are correct. For example, no provisions are made to make sure that Net::DNS::RR::A record contains a valid IP address. This is supposed to be done beforehand by some other means.

NOTE

Please note, that API of the module as of version 0.01 is in flux and incompatible changes may be introduced in the future versions.

check

The check() sub takes the following named parameters:

zone

A name of the zone being checked. This is a required parameter.

records

A reference to an array of Net::DNS::RR records representing zone contents. This is a required parameter.

on_error

Optional parameter, the default value is undef. For the description see section about error handling below.

soft_error

Optional parameter, the default value is false. For the description see section about error handling below.

quiet

Optional parameter, the default value is false. For the description see section about error handling below.

strict_checks

Optional parameter, the default value is false. If set to true, check() will also perform some checks for conditions that strictly speaking are not errors, but which better to avoid nevertheless.

relaxed_checks

Optional parameter, the default value is false. If set to true, check() will not perform some checks for conditions which are not very critical. If strict_checks is set, relaxed_checks is assumed to be false, whether it is specified or not.

network_checks

Optional parameter, the default value is false. If set to true, check() will perform some checks which involve DNS lookups.

It is also possible, if no other parameters are specified, to call check() using positional parameters as

check($records, $zonename);

The check() function goes through zone records and performs the following checks:

-

that any SOA records are defined for the zone;

-

that there is exactly one SOA record;

-

that SOA record is indeed an SOA record for this zone;

-

that there are some NS records;

-

that all NS records are inside this zone;

-

that NS records do not point to an IP address (can be skipped with relaxed_checks);

-

that NS records will be visible, which might not be the case because of delegations (can be skipped with relaxed_checks);

-

that, if an NS record points outside of the zone itself, a successful DNS lookup for the corresponding A record can be made (only done when network_checks is true);

-

that, if an NS record points inside the zone, it points to an existing A record (can be skipped with relaxed_checks);

-

that, if an NS record is a subzone delegation that points to the subzone itself or inside the subzone, a corresponding "glue" A-record exists (can be skipped with relaxed_checks);

-

that there are at least two NS records for the zone itself and for every subzone (only done when strict_checks is true);

-

that there are no duplicate identical records (only done when strict_checks is true);

-

that records with identical name and type do not have different TTLs (only done when strict_checks is true);

-

that there are no records with names outside of the zone;

-

that there are no "invisible" records due to existing delegations (can be skipped with relaxed_checks);

-

that there is no "CNAME and other data" condition;

-

that there are no multiple CNAME records with the same name (this is in fact a particular case of "CNAME and other data" condition);

-

that a CNAME record does not point to an IP address (can be skipped with relaxed_checks);

-

that a CNAME record does not point to a non-existing record inside the zone;

-

that a CNAME record does not point to another CNAME record inside the zone (can be skipped with relaxed_checks);

-

that a CNAME record points to an A record (the previous condition is an exception from this, if relaxed_checks is true);

-

that an MX record does not point to an IP address (can be skipped with relaxed_checks);

-

that an MX record does not point to a non-existing record inside the zone;

-

that an MX record does not point to a CNAME record inside the zone (can be skipped with relaxed_checks);

-

that an MX record points to an A record (the previous condition is an exception from this, if relaxed_checks is true);

-

that a PTR record does not point to an IP address (can be skipped with relaxed_checks);

-

that a PTR record does not point to an IN-ADDR.ARPA or IP6.INT zone.

It is expected that more checks will be added in the future.

Error handling

The error handling provided by check() is simplistic, but convoluted. There are three parameters that control what exactly happens when an error occurs, on_error, soft_errors, and quiet. The on_error parameter allows the caller to specify a custom error handler. If it is set, it is always called on any error. When called, it is passed a hash, in which three key-value pairs are of relevance: error, a textual description of the error, error_record, a DNS::RR which is related to the error, if applicable, and zone, a name of the zone passed by the caller of check(). The interplay of on_error, soft_errors, and quiet is best described by the following table:

on_error is set
      quiet        true        false         !exist
   soft_errors
      true        return     warn & return   return
      false       die        warn & die      die
      !exist      return     warn & return   return

on_error is not set
      quiet        true        false         !exist
   soft_errors
      true        return     warn & return  warn & return
      false       die        warn & die     warn & die
      !exist      die        warn & die     warn & die

The values of both soft_errors and quiet when they are not specified are effectively reset to true if on_error is set (compare true/true and !exist/!exist in the first table). The author thinks that this behavior is the most sensible (remember that on_error is always called when set).

compare

The compare sub takes two parameters which must be references to arrays of Net::DNS::RR records. It is assumed that both parameters represent the same zone, probably at different moments of time. The subroutine determines what records were added, removed, and modified in the second input array in comparison with the first. It returns three values representing the changed records, the added records, and the removed records. The last two returned values are references to arrays of Net::DNS::RR records, while the returned value for that represents modified records is a reference to an array of references to hashes, each containing exactly two pairs: one old key with a Net::DNS::RR record from the first input parameter, and another with new key with a Net::DNS::RR record from the second input parameter.

HISTORY

The basic module functionality was first described as a collection of small examples in my talk "Keeping your DNS sane with Perl" presented at Nordic Perl Workshop 2004 in Copenhagen. Nicholas Clark suggested to me to make a module out of it.

COPYRIGHT AND LICENSE

Copyright 2004 by Anton Berezin

"THE BEER-WARE LICENSE" (Revision 42)
<tobez@tobez.org> wrote this module.  As long as you retain this notice
you can do whatever you want with this stuff. If we meet some day, and
you think this stuff is worth it, you can buy me a beer in return.

Anton Berezin

CREDITS

This module was largely inspired by a module of a similar functionality implemented by me for catpipe Systems ApS as a part of a DNS management system for France Telecom.

SEE ALSO

Net::DNS(3), Net::DNS::RR(3), Net::DNS::ZoneFile(3), Net::DNS::ZoneFile::Fast(3).