Design and Implementation of DelphinusDNSD

  1. What is DNS?
  2. Recursive and Authoritative Nameservers
  3. Delphinusdnsd
  4. DelphinusDNSd timeline
  5. Resource Records
  6. Design obstacles
  7. Design obstacles with DNSSEC
  8. Internal Database holding RR's

What is DNS?

DNS is an acronym for Domain Name System. In laymans terms it means, it does the looking up of domain names on the Internet. In professional circles, looking up is called resolving. So a domain name resolves if a DNS server exists and is configured on the Internet, serving it.

What is a domain name? delphinusdns.org, centroid.eu are domain names. Just "delphinusdns" and "centroid" are called dns labels, same with org and eu. A domain name with the name of "www.centroid.eu." has three labels then where the .eu label is also called the Top Level Domain or TLD. Centroid inside "www.centroid.eu." is called 2nd level domain and www is also called 3rd level domain.

Really a domain name has a trailing dot, like "www.centroid.eu." this indicates that there is a root. The root looks sort of like this...





                        . (root)
                        |
                        |--  com
                        |     `--  example
                        |             `--  www
                        |--  org
                        |     |--  delphinusdns
                        |     |          `--  pod
                        |     `--  example
                        |             `--  www
                        |--  eu
                        |     |--  centroid
                        |     `--  dtschland
                        `--  de
                             |--  denic
                             `--  mainrechner
                                       `--  www

             [1] The (partial) DNS tree showing the root at top.

When a domain name lookup is made for "centroid.eu." first the root is consulted for the .eu nameservers, then the .eu nameservers are consulted for the centroid.eu. domain name. It is a distributed database in other words, because the nameservers along the way can belong to different organizations and networks. Please see iana.org for different TLD's.

Similarily in DNSSEC (DNS Security) in order to get the cryptographically authentic answer to "www.centroid.eu." in simplified terms (pretend there is only KSK's) the centroid.eu nameservers are asked for the DNSKEY RR which is a public cryptography key. The www label has an RRSIG RR with the signature of the A RR (for www). If this checks out the DNSKEY of centroid.eu is crosschecked with the DS RR hash at .eu, which also has an RRSIG RR and is verified against the DNSKEY for .eu (here a validation for the DS RRSIG is performed through public key cryptography) and crosschecks that with the DS RR hash at the root (again this DS's RRSIG is validated against the root's DNSKEY RR). Finally at the root it is the end of a long chain, so we call this an anchor. The anchor has to be well known in DNSSEC validation implementations. When the anchor (root) changes its DNSKEY, validation software must update this (it's usually hardcoded). Root (KSK) key changes happen infrequently. This way a domainname is deemed as authentic when all checks out (and validates along the way) in this anchored chain. DNS caching servers can cache the RR's for this process speeding it up on subsequent validations.

Terminology

Recursive and Authoritative Nameservers

A nameserver that looks up entire domain names from the root, is said to be a recursive nameserver. If it checks the DNSSEC signatures of the answers it is a validating (and recursive) nameserver. A nameserver that only answers for the domain that it is authoritative for is an authoritative nameserver. Both types have a place on the Internet with recursive nameservers being BIND, Unbound, PowerDNS etc. Others serve as authoritative nameservers such as BIND, nsd, djbdns, PowerDNS authoritative and many more.

Turnkey vs. General Purpose DNS Server

RFC 5936 mentions two specific types of Implementations on page 4.

   "General-purpose DNS implementation" refers to DNS software developed
   for widespread use.  This includes resolvers and servers freely
   accessible as libraries and standalone processes.  This also includes
   proprietary implementations used only in support of DNS service
   offerings.

   "Turnkey DNS implementation" refers to custom-made, single-use
   implementations of DNS.  Such implementations consist of software
   that employs the DNS protocol message format yet does not conform to
   the entire range of DNS functionality.
So then BIND is a General-purpose DNS implementation. Delphinusdnsd is half way between Turnkey and General-purpose.

Delphinusdnsd

Delphinusdnsd was conceived in 2005 and laid down on November 29th, 2005. It was designed to give false answers inside a firewalled intranet. Why this was done, was so that DNS could be isolated but still give rudamentary answers. A program called authpf would switch between delphinusdnsd and BIND to give lookups to a firewalled and then authenticated Wifi user. At first delphinusdnsd was programmed on OpenBSD and on April 10th, 2008 it was ported to Linux. At that time it ran on NetBSD, FreeBSD, DragonflyBSD, OpenBSD and Redhat Linux.

The concept of a firewalled DNS doesn't fly very well with caching name resolvers that are found on Windows and Solaris, so I just kept hacking away with the goal of a General Purpose Nameserver in mind.

Delphinusdnsd tries not to reuse too much code, especially in the DNS operations. Importing DNS code from other projects is a nono. I want to build something on my own because I want to show a robustness stemming from an independent source. This means if there is a bug in BIND it won't be copied over in Delphinusdnsd.

Delphinusdnsd is now an authoritative nameserver. It serves both IPv4 and IPv6 since its inception. It can be configured to serve a number of Resource Records (RR's). With the -l option set on delphinusdnsd all queries made to it are logged. What's logged is the time of day, remote IP/IP6 of the recursive resolver, the RR in question and the domain name being looked up.

DelphinusDNSD timeline

First 15 years


2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---->
[1]            [2]  [3,4][5,6,7][8] [9]    [A] [B,C] [D]   [E,F,G] [H][I,J]

[1] November 29th, 2005  - original implementation / check-in
[2] April 10th, 2008     - Linux Port
[3] April 12th, 2009     - rewrite of compression routines
[4] November 3rd, 2009   - Round Robin DNS
[5] March 12th, 2010     - TCP
[6] March 27th, 2010     - split-horizon DNS
[7] December 27th, 2010  - ANY RR type
[8] September 19th, 2011 - AXFR
[9] April 30th, 2012	 - SRV 
[A] October 18th, 2013	 - Raspberry Pi support
[B] April 2014	 	 - YACC parser, SSHFP RR, SPF RR
[C] May 2014		 - EDNS0, NAPTR RR, Notify
[D] December 31st, 2015	 - 1.0.0 Release (10 year anniversary release)
[E] January 28th, 2017	 - 1.1.0 Release (dd-convert.c)
[F] March 14th, 2017 	 - removal of SPF RR
[G] June 27th, 2017	 - removal of BerkeleyDB for tree(3) and the
			   registration of delphinusdns.org
[H] July 17th, 2018	 - 1.3.0 Release (dddctl.c)
[I] January 10th, 2019	 - KSK rollover support
[J] February 2019	 - New Database and TSIG support

Observances:

Past year 2020


2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---->
[1][2]  [3,4][5]     [6]         *


[1] January 2nd, 2020   - Delphinusdnsd 1.4.0 release
[2] November 24th, 2020 - Delphinusdnsd 1.5.0 release
[3] December 20th, 2021 - Delphinusdnnd 1.6.0 release
[4] March 22nd, 2022    - Delphinusdnsd 1.6.1 release
[5] December 2nd, 2022  - Delphinusdnsd 1.7.0 release
[6] February 16th, 2024 - Delphinusdnsd 1.8.0 release
[*] 2026 - Availability of Windows Port (hopefully)

Observances:

Resource Records

With help of gotweb and cvs2git conversion programs I was able to reconstruct how delphinusdnsd grew over time. Much respect to myself in November 2005 for writing the then named wildcarddnsd in a few weeks time (I'm guessing 3 to 5). Below shows the addition of RR's in some chronology.

Design obstacles

Design obstacles with DNSSEC

DNSSEC was added in 2015, where I basically coded every single time I had off. To do DNSSEC effectively, EDNS has to work right. In version 1.0.0 delphinusdnsd will reply a BADVERS packet to any version other than 0. We're actually EDNS compliant, (see here: https://ednscomp.isc.org/ednscomp/5a70566c18). Also all 7 extra RR's (DNSKEY, RRSIG, NSEC, NSEC3, NSEC3PARAM, DS, TLSA) have been added to do DNSSEC and DANE. The code had to be modified in reply_* to add an RRSIG record when dnssec is enabled. (NSEC is shaky since we went straight with RFC 5155 NSEC3 for signing and use).

Also reply_nxdomain had to be modified to give NSEC3 answers. The way these DNSSEC RR's make it into the server is to run the zone file through a tool which does conversions to DNSSEC. In version 1.0.0 I created a ruby tool called dd-convert.rb, which called the BIND tools, dnssec-keygen and dnssec-signzone, and then converts that back to delphinusdnsd zonefile format. I also had sponsored Luke Antins who added DNSSEC support to his ruby gem dns-zone which I use to parse the BIND zonefile. A second sponsorship to get TLSA support into dns-zone, to have DANE support, never materialized.

So then in version 1.1.0 I redid the ruby tool into a C tool that doesn't rely on BIND tools, and does TLSA signing for DANE. Ruby is not being used anymore but it was perhaps a good prototyping program. The NSEC RR support has not ever been tested and could be buggy, in fact you cannot dd-convert to anything other than NSEC3 support by default, changing this means changing the code.

DNSSEC errors

In 2016 some DNSSEC errors were detected in the Implementation:

In 2019 key rollovers were programmed, however:

Supported DNSSEC Algorithms in Delphinusdnsd

RFC 8624 has a chart like this in section 3.1:

   +--------+--------------------+-----------------+-------------------+
   | Number | Mnemonics          | DNSSEC Signing  | DNSSEC Validation |
   +--------+--------------------+-----------------+-------------------+
   | 1      | RSAMD5             | MUST NOT        | MUST NOT          |
   | 3      | DSA                | MUST NOT        | MUST NOT          |
   | 5      | RSASHA1            | NOT RECOMMENDED | MUST              |
   | 6      | DSA-NSEC3-SHA1     | MUST NOT        | MUST NOT          |
   | 7      | RSASHA1-NSEC3-SHA1 | NOT RECOMMENDED | MUST              |
   | 8      | RSASHA256          | MUST            | MUST              |
   | 10     | RSASHA512          | NOT RECOMMENDED | MUST              |
   | 12     | ECC-GOST           | MUST NOT        | MAY               |
   | 13     | ECDSAP256SHA256    | MUST            | MUST              |
   | 14     | ECDSAP384SHA384    | MAY             | RECOMMENDED       |
   | 15     | ED25519            | RECOMMENDED     | RECOMMENDED       |
   | 16     | ED448              | MAY             | RECOMMENDED       |
   +--------+--------------------+-----------------+-------------------+
Currently Delphinusdnsd can only do algorithms 8, 10, 13, 14 and 15. Alg 14 and 15 support will be released with 1.8 release.

Internal Database holding RR's

New Design

This is somewhat what the new internal database looks like:

+-------------+
|struct rbtree|
+-------------+
        |
        | centroid.eu
        |	+------------+	  		  +------------+
        +-------|struct rrset|--------------------|struct rrset|---> NULL
        |	+------------+	                  +------------+
        |	     |		       		       |
        |	     |		       		       |
        |	+----------+			  +----------+
        |	| struct rr|->struct soa 	  | struct rr|->struct ns
        |	+----------+			  +----------+
        |	     |		       		       |
        |	     v		       		       |
	|	    NULL	     		  +----------+
        |	      		       		  | struct rr|->struct ns
	|	   				  +----------+
	|					       |
	|					       v
	|					      NULL
        | name.centroid.eu
        |	+------------+	
        +-------|struct rrset|---> NULL
        |	+------------+	
        |	      |		    
        |	      |		   
        |	+----------+
        |	| struct rr|->struct a
        |	+----------+	
        |	      |		   
        |	      v		   
        |	    NULL 
        |	     
	v
There is a struct rbtree which is a node in the RB TREE, it has a pointer to a TAILQ of type struct rrset, which in turn has a pointer to a TAILQ of type struct rr. From there a void * pointer points to the type of struct it is which is also indicated in struct rrset.

Reason

The reason this was done was to save memory, and compared to an NSD slave it does brilliantly.