% WTDNS(8) - What the DNS?
% Jonas Eckerman <jonas@truls.org>
% 2012-10-16 16:48:21 UTC (v0.018)

# NAME

wtdns - What the DNS?

# SYNOPSIS

WtDNS is a master DNS server coupled with utilities for maintaining adynamic
DNS system.

# DESCRIPTION

WtDNS is a system that consists of a small authoritative DNS server (WtDNSd),
a DNS record updater that gets updates requests via a webserver (or something
else) without having to be started for each update request and a DNS record
maintainer that replaces or deletes records when they haven't been update for
a set duration.

WtDNSd works with zone records stored on a one-type-per-file basis, wich makes
it simple to configure, easy to update, and efficient for systems with very
frequent updates (such as a dynamic DNS service). You do not have to restart,
reload or refresh WtDNSd for updates since any updates are immediately
available in the file system. WtDNS is _not_ a caching or recursive DNS
server and has no built in resolver.

The DNS resource records are stored in files named

	/var/lib/wtdns/#XX/fqdn.hostname.domain/TYPE

or (when not using hashes)

	/var/lib/wtdns/fqdn.hostname.domain/TYPE

where fqdn.hostname.domain is a lower case full domain name, or an IP address
for PTR lookups, XX is a upper case hexadecimal hash of the domain name, and
TYPE is a upper case DNS RR type. An entry such as "\*.example.com" will return
results as expected.

The records can be created with the **wtdnsadd** utility, or edited with a
text editor. The **wtdnshash** utility can be used to create and find the
directories, or alternatively WtDNS can be told to omit the hash part of the
path.

WtDNSd does not do error checking on the records and will answer with their
contents even if they are bogus as long as it manages to format them
according to the standards.

## Lookups

WtDNSd does lookups in lower case. All host name directories and and host
names in file contents must be in lower case. File base names (query types)
are in upper case.

Wild card records are specified by creating a record for a hostname where the
first part is an asterisk '\*'. A record for "\*.example.com" will be served for
"one.example.com", "other.example.com" etc, unless they have the record in
question.

**wtdnsd** considers itself authoritative for all domain names for wich it can
locate an an SOA record. For both NS and SOA records, an entry for a domain is
valid for all domains below it. For example, if you create an SOA record for
the domain example.edu, then that SOA record is used for the domain 
www.example.edu, ftp.henry.example.edu, as well as
user1.lab.chemistry.example.edu. On the other hand a separate SOA record can
be created for lab.chemistry.example.edu, applying to all domains
"\*.lab.chemistry.example.edu", "\*.\*.lab.chemistry.example.edu" etc. This
works because when checcking or adding to the authority section, WtDNSd
searches for NS and SOA records by iteratively deleting the text up to the
front most-dot until a nameserver is found.

## Message Size

If not using EDNS, WtDNSd does not send UDP results longer than the mandated
512 bytes. If using EDNS, WtDNSd does not send unsecure UDP results longer
1280 bytes or secured DNS records longer than 4096 bytes.

WtDNSd does not send results longer than 4096 bytes over TCP.

If there is too much data to fit in the message, then trailing records are
omitted to keep within the limit, and the packet format will be valid.
Administrators should ensure that their site is not so complex that large
packets need to be sent.

WtDNSd does not serve recieved UDP or TCP messages longer than 1280 bytes. If
a received message is too long, WtDNSd answers "refused".

## Zones

WtDNSd has no concept of zones. The system simply serves (autoritatively)
whatever records it finds in tne record store, wether it really should be
responsible for the domain or not.

Since WtDNS has no concept of zones, WtDNSd cannot do zone transfers. A slave
WtDNSd can be kept in sync using an efficient file syncronizer such as rsync.
Since WtDNSd serves records from disk, it is very suitable for this. For fast
slave syncronization, a file change monitor such as Lsyncd can be used.

## DNSSEC

WtDNS supports signing and serving for DNSSEC-bis. Signing can be done with
the **wtdnssign** utility wich saves records in the format used by WtDNSd, or
with other tools followed by manual conversion of the record formata.

DNSSEC processing automatically adds some records to responses from WTDNSd:

Resource record signatures

:	RRSIG RRs are added, if found, for each record in the response.

Next secure

:	NSEC RRs are added, if found, whenever an exact match for a domain
	name could not be found (including when a wildcard match was found).
	When looking for NSEC records, **wtdnsd** will only look in the
	domain and it's parents. When using **wtdnssign** to sign a domain,
	a fake NSEC (in violation of strict DNSSEC) is created to avoid
	problems caused by this.

Delegation signer

:	DS RRs are added, if found, for SOA and NS record is included in the
	answers and authority sections.

## Resource Records

Each resource record file contains one or more newline separated strings.
There should be no leading newlines, and zero or one trailing newline. The
maximum size of a resource file is 1024 bytes.

When records contain more than one, those fields can be separated by ';'
and/or ':'. A string can be enclosed in '"', "'", "[]" or "{}" to avoid
separation. Enclosing '"' and  "'" will be stripped from the string, while
"[]" and "{}" will be left where they are. To prevent any special characters
from having any effect escape it with '\'. For example:

	[field:one];" field;two"; field three:field four :{field\}five};field\\six:' field seven'

would give the following fields:

	[field:one]
	field;two
	fieldthree
	fieldfour
	{field}five}
	field\six
	fieldseven

A records

:	In A records the strings are IPv4 addresses, for example "192.0.2.1".
	You can have multiple lines per file. If there are several addresses
	in the file, they are order-randomized before return.

AAAA records

:	In AAAA records, the strings are IPv6 addresses, for example
	"2001:DB8:4F0B:1F1D:6795:941A:7E73:CFA6".
	If no AAAA record file is found but there is an A6 record file,
	records with zero-length prefixes from the A6 record file are used.
	You can have multiple lines per file. If there are several addresses
	in the file, they are order-randomized before return.

A6 records

:	In A6 records, the strings are either IPv6 addresses or a prefix
	length, followed by a IPv6 address, followed by domain name, for
	example
	"2001:DB8:4F0B:1F1D:6795:941A:7E73:CFA6" or
	"8; 2001:DB8:4F0B:1F1D:6795:941A:7E73:CFA6; ns.example.net". For
	obvious reasons the fields can not be separated with ':'.
	If no A6 record file is found but there is an AAAA record file,
	records from the AAAA record file are used.
	You can have multiple lines per file. If there are several addresses
	in the file, they are order-randomized before return.

PTR and CNAME records

:	The string is a host-name.
	You must have only one entry in each file.

NS records

:	The strings are host-names.
	An entry for a domain is valid for all domains below it.
	You can have multiple lines per file.

MX records

:	The strings are host-names.
	You can have multiple lines per file. The first entry in the file is
	given a priority of 10, the second 20, and so on.

SRV records

:	The strings contains a hostname and port, for example
	"voip.example.com:5060".
	You can have multiple lines per file. The first entry in the file is
	given a priority of 10, the second 20, and so on. All entries are
	given the weight 0.

SOA records

:	The string is a host-name, optionally followed by an email address.
	For example "ns.example.net" "ns.example.net:admin.example.com".
	An entry for a domain is valid for all domains below it.
	You must have only one entry in each file.

	
	The modified time of the file counts as the serial-number, and the
	contents as the name-server and administrator email address. If no
	email address has been specified, the address returned is just the
	name-server prepended with "hostmaster". Any '@' in the email address
	will be replaced with a '.'. The remaining fields are hardcoded to
	reasonable default values. Note that time fields for SOA records are
	only used for zone transfers hence are irrelevant here.

TXT records

:	TXT records contain arbitrary text. When sent they will be split into
	chunks of at most 255 characters each.
	If a TXT record equals a macro string, it will be replaced with the
	expanded macro.


SPF records

:	SPF records are basically TXT records but must follow the SPF
	specifications and can't contain macros.

HINFO records

:	A HINFO record should contain two fields, a CPU identifier followed by
	a system (OS) identifier.

DNSKEY records

:	A DNSSEC public key record contains four fields. The key flags,
	key protocol and key algorithm as decimal integers, followed by the
	key data encoded with Base64. Note that as with other records, the
	whole record must be on one line, so the Base64 data must not be
	line-broken.
	You can have multiple lines per file.

DS records

:	A DNSSEC delegation signer record contains four fields. The key tag
	and key algorithm, for the referenced key, and the digest type, as
	decimal integers, followed by the key digest in hexadecimal format.
	You can have multiple lines per file.

RRSIG records

:	A RRSIG record contains 8 fields. The key algorithm, the count of
	labels in the signed domain name and the TTL of the signed record as
	decimal integers, the spiration time and inception time in the format
	YYYYMMDDhhmmss, the key tag as a decimal integer and the key owner
	domain name, followed by the signature encoded with Base64. Note that
	as with other records, the whole record must be on one line, so the
	Base64 data must not be line-broken.
	RRSIG record file names differ from other records, being a
	concatenation combination of the signed records RR type and RRSIG
	separated with a';'.
	You can have multiple lines per file.

## TXT Macros

A TXT record (one line in a TXT record file) can be replaced with a macro. For
this to be done the line must consist only and totally of the macro, wich has
the following syntax:

	%%wtdns:macrotag%%

where "macrotag" is the name of the macro. For a list of defined macros, see
wtdnsd(8). There could be an example TXT record file with macros installed to
your WtDNS documentations var directory.

# PROGRAMS

wtdnsd(8)
:	The actual name server daemon that answer queries from remote systems.

wtdnsinfo(8)
:	Query a running **wtdnsd** for status infromation.

wtdnsadd(8)
:	Manually add resource records to the data store.

wtdnssign(8)
:	Sign record setss and zones according to DNSSEC-bis.

wtdns-fromlog(8)
:	A dynamic DNS updater intended to be used with an authenticating web
	server, but can be used with anything that can give it log lines in
	the correct format.

wtdns-maintain(8)
:	Deletes and replaces old records in the dynamic domain name system.

wtdnshash(8)
:	Find and create hash directories in the data store for host names.

wtdns-deepen(8)
:	Convert a data store not using hash directories to one using them.

wtdns-flatten(8)
:	Convert a data store using hash directories to one not using them.

# FILES

/var/lib/wtdns
:	All resource records are stored in this directory.

/etc/wtdns/wtdns.ini
:	The global configuration file.

/etc/wtdns/apache.conf
:	Example configuration for dynamic DNS update service.

# BUGS

If you find any, please send a report to <jonas@truls.org>.

# NOTES

The WTDNS server and hasher is a fork of SheerDNS.
SheerDNS was written by Paul Sheer.

Updated WtDNS packages could be available at <http://whatever.truls.org/>.

# EXAMPLE

These examples show how you can create records manually, and serves as
examples of how you may implement record creating in your own scrips. For more
simple updating of records, you can use the **wtdnsadd** utility.

The examples assume that you have already invoked **wtdnsd**, that you know
what kind of records are appropriate to create, that you do not guess, and
that you realize that the examples are merely shown for demonstration and not
as a recommendation for the kinds of records really required.

Add a wildcard entry for "\*.example.com". Note that quotes around the
asterisk wich are needed because the filename has an asterisk in it:

	echo 192.0.2.1 > /var/lib/wtdns/`wtdnshash '*'.example.com`/'*'.example.com/A
	dig @localhost A lsdjkfhalsdfkjh.example.com

Add mutiple A records for "example.com" for round robin sharing:

	echo 192.0.2.1 > /var/lib/wtdns/`wtdnshash example.com`/example.com/A
	echo 192.0.2.2 >> /var/lib/wtdns/`wtdnshash example.com`/example.com/A
	echo 192.0.2.3 >> /var/lib/wtdns/`wtdnshash example.com`/example.com/A
	dig @localhost A example.com
	dig @localhost A example.com
	dig @localhost A example.com

Add a PTR records for "example.com":

	echo example.com > /var/lib/wtdns/`wtdnshash 192.0.2.1`/192.0.2.1/PTR
	echo example.com > /var/lib/wtdns/`wtdnshash 192.0.2.2`/192.0.2.2/PTR
	echo example.com > /var/lib/wtdns/`wtdnshash 192.0.2.3`/192.0.2.3/PTR
	echo ns.example.net > /var/lib/wtdns/`wtdnshash 192.0.2`/192.0.2/NS
	dig @localhost PTR 1.2.0.192.in-addr.arpa

Add an MX record for example.com with both its forward, reverse, and
nameserver lookups:

	echo mail.example.com > /var/lib/wtdns/`wtdnshash example.com`/example.com/MX
	echo 192.0.2.99 > /var/lib/wtdns/`wtdnshash mail.example.com`/mail.example.com/A
	echo mail.example.com > /var/lib/wtdns/`wtdnshash 192.0.2.99`/192.0.2.99/PTR
	echo ns.example.net > /var/lib/wtdns/`wtdnshash example.com`/example.com/NS
	dig @localhost MX example.com

Add an SOA record for "example.com" with the default administrator email
address "hostmaster@example.net", and one for "division.example.com" with
administrator email address "divmaster@example.com". Note that although SOA
records are only required for zone transfers, some institutions may demand
them. The only configurable data items in the SOA record are the authoritative
nameserver and administrator email address, wich are inserted as follows:

	echo ns.example.net > /var/lib/wtdns/`wtdnshash example.com`/example.com/SOA
	echo ns.example.net:divmaster.example.com > /var/lib/wtdns/`wtdnshash division.example.com`/division.example.com/SOA
	dig @localhost SOA example.com
	dig @localhost SOA division.example.com

Add a CNAME record for "www.example.com". If you use a CNAME, you should only
have one line in the CNAME file, and it should be the only file in the
domain's directory, and the CNAME should not appear anywhere as the text of
any other records.

	# Remove existing records for www.example.com
	rm -f /var/lib/wtdns/`wtdnshash www.example.com`/www.example.com/*
	# Check if some other record points to www.example.com
	grep -w 'www[.]test[.]com' `find /var/lib/wtdns/ -type f` && echo "This DNS setup is broken"
	# Add the record
	echo example.com > /var/lib/wtdns/`wtdnshash www.example.com`/www.example.com/CNAME
	dig @localhost A www.example.com

Using WtDNS as a root nameserver. Note that for root domains, the hash is
especially omitted:

	> /var/lib/wtdns/NS
	> /var/lib/wtdns/SOA
	for i in  \\
	 a:198.41.0.4     h:128.63.2.53     c:192.33.4.12     g:192.112.36.4    \\
	 f:192.5.5.241    b:128.9.0.107     j:192.58.128.30   k:193.0.14.129    \\
	 l:198.32.64.12   m:202.12.27.33    i:192.36.148.17   e:192.203.230.10  ; do
	    N=`echo $i | cut -f1 -d:`.root-servers.net
	    echo $i | cut -f2 -d: > /var/lib/wtdns/`wtdnshash $N`/$N/A
	    echo $N >> /var/lib/wtdns/NS
	    echo $N >> /var/lib/wtdns/SOA
	done
	dig @localhost SOA .
	dig @localhost NS .
