# DNS and DNSSEC zone file validator.

`validns` reads a DNS zone in the master file format of RFC 1035 section 5.1
and checks it for correctness. It parses every record and directive,
reporting the exact reason for anything it cannot parse, and then performs
a series of structural, consistency, and - for signed zones - DNSSEC checks
on the parsed data.

For a DNSSEC-signed zone, `validns` verifies each RRSIG against the
corresponding DNSKEY, checks that signatures are within their validity period,
and walks the NSEC or NSEC3 authenticated-denial chain to confirm it is
complete and consistent with the records actually present in the zone.
The basic and DNSSEC checks are always performed; additional operator policy
checks are enabled individually with -p (see POLICY CHECKS, below).

The intended use is to validate a zone before it is published, for example
as a step in a zone-signing or provisioning pipeline. By default `validns`
processes the whole zone and reports every problem it finds; `-f` makes
it stop at the first error. It exits with a non-zero status if any errors
were reported and zero otherwise.

## Requirements

- Judy dynamic arrays
	- http://judy.sourceforge.net/
	- FreeBSD: ports/devel/judy
	- Debian/Ubuntu: libjudy-dev
	- MacOS X: brew install judy
- Test::Command::Simple perl module (for tests only)
	- FreeBSD: ports/devel/p5-Test-Command-Simple
	- anywhere: cpanm Test::Command::Simple

## Build and install

```shell
git clone https://codeberg.org/DNS-OARC/validns
cd validns
sh autogen.sh
./configure
make
make install
```

## CHECKS

Please see `man validns` for the exact checks that are available for the
version you are running.

### BASIC CHECKS

Every record and every supported directive should be parsable, which
consitutes the most basic check of all. The `validns` program will report
the exact reason why it cannot parse a record or a directive.

Other basic checks include:

- there could only be one SOA in a zone;
- the first record in the zone must be an SOA record;
- a record outside the apex;
- TTL values differ within an RR set (excepting *RRSIG*);

### DNSSEC CHECKS

- *type* exists, but NSEC does not mention it for *name*;
- NSEC mentions *type*, but no such record found for *name*;
- NSEC says *x* is the last name, but *z* exists;
- NSEC says *z* comes after *x*, but nothing does;
- NSEC says *z* comes after *x*, but *y* does;
- signature is too new;
- signature is too old;
- RRSIG exists for non-existing type *type*;
- RRSIG's original TTL differs from corresponding record's;
- RRSIG(*type*): cannot find a signer key;
- RRSIG(*type*): cannot verify the signature;
- RRSIG(*type*): cannot find the right signer key;
- NSEC3 record name is not valid;
- multiple NSEC3 with the same record name;
- no corresponding NSEC3 found for *name*;
- *type* exists, but NSEC3 does not mention it for *name*;
- NSEC3 mentions *type*, but no such record found for *name*;
- there are more record types than NSEC3 mentions for *name*;
- broken NSEC3 chain, expected *name*, but nothing found;
- broken NSEC3 chain, expected *name1*, but found *name2*;
- NSEC3 without a corresponding record (or empty non-terminal);
- *name* *type*: no covering RRSIG in a signed zone;
- *algorithm* signatures cannot be validated: *digest* is disabled by system crypto policy;

On systems where cryptographic algorithms are restricted by policy (e.g.,
RHEL 9/CentOS 9 with SHA-1 disabled), `validns` detects this and reports
which algorithms cannot be validated. Affected signatures are skipped rather
than producing cryptic errors.

### POLICY CHECKS

The following policy checks are understood:

**single-ns**

: Checks that there are at least two NS records per name (or zero).

**cname-other-data**

: Checks for CNAME and other data condition (excluding possible and NSEC).

**dname**

: DNAME checks: no multiple DNAMEs, no descendants of a node with a DNAME.
  Please note that DNAME/CNAME clash is handled by CNAME and other data check
  already.

**dnskey**

: DNSKEY checks: public key too short, leading zero octets in public key
  exponent or modulus.

**nsec3param-not-apex**

: NSEC3PARAM, if present, should only be at the zone apex.

**mx-alias**

: MX exchange should not be an alias.

**ns-alias**

: NS nsdname should not be an alias.

**rp-txt-exists**

: TXT domain name mentioned in RP record must have a corresponding TXT record
  if it is within the zone.

**tlsa-host**

: Domain name of a TLSA record must be a proper prefixed DNS name.

**ksk-exists**

: A KSK key must exist in a signed zone.

**smimea-host**

: Domain name must have the form which is proper for an SMIMEA record.

**all-algorithms-signed**

: In a signed zone, every RR set that must be signed must have covering
  RRSIG records for every algorithm present in the apex DNSKEY RR set
  (RFC 4035, section 2.2).

**occluded-records**

: Records hidden below a zone cut (occluded by a delegation) are reported.
  Address records serving as glue are exempt: those referenced from the NS
  RR set of the delegation above them, or from the apex NS RR set. Glue
  referenced only by some other delegation ("sibling glue") is still
  reported; combine with **no-strict-glue** to permit it.

**missing-glue**

: Every delegation whose name server names fall at or below the delegation
  point must have A or AAAA glue records for them.

**all**

: Activates all of the above policy checks (but not permit-starting-hyphen and no-strict-glue).

**permit-starting-hyphen**

: Starting hyphen (normally disallowed) is permitted in names.

**no-strict-glue**

: Modifies **occluded-records** to also accept glue referenced by any NS
  record in the zone, not just the delegation the glue sits under or the
  apex NS RR set.

### OMISSIONS

A number of corners were cut to assume the most usual way of doing things.
Therefore, in many cases, `validns` currently does not strictly adhere to
various standards.

In particular, it should be possible (and easy) to construct a perfectly
valid zone file which `validns` will report as problematic. It is expected
that those cases will be all fixed over time.

But if you have a valid zone which `validns` cannot parse, please do report
this fact to the author, with examples. If there is a need for the community
to fix a particular omission, it will be fixed sooner. Needless to say,
patches are always welcome.

## BUGS

- textual segments in *TXT* and *HINFO* must be enclosed in double quotes;

If at least one NSEC3 record uses opt-out flag, `validns` assumes it is used
as much as possible, that is, every unsigned delegation does not have
a corresponding NSEC3 record.
This is done for reasons of efficiency, to avoid calculating cryptographic
hashes of every unsigned delegation. If this assumption is wrong for a zone,
`validns` will produce spurious validation errors.

## ACKNOWLEDGEMENTS

Thanks go to
Andy Holdaway,
Daniel Stirnimann,
Dennis Kjaer Jensen,
Göran Bengtson,
Hirohisa Yamaguchi,
Hugo Salgado,
Jake Zack,
Jakob Schlyter,
Koh-ichi Ito,
Mathieu Arnold,
Miek Gieben,
Patrik Wallström,
Paul Wouters,
Ryan Eby,
Tony Finch,
Willem Toorop,
and YAMAGUCHI Takanori
for bug reports, testing, discussions,
and occasional patches.

Special thanks to
Stephane Bortzmeyer and Phil Regnauld.

Thanks for AFNIC which funded major portion of the development.
Thanks for SWITCH for additional funding.
