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

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

                                .  root     
                               / \
                              /   \
                             /     \
                          org       eu
                         /   \     /  \
                        /              \
                    delphinusdns      centroid

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

When a domain name lookup is made for "" first the root is consulted for the .eu nameservers, then the .eu nameservers are consulted for the 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 for different TLD's.


Recursive and Authoritative Nameservers

A nameserver that looks up entire domain names from the root, is said to be a recursive nameserver. A nameserver that only answers 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, djbdns, etc. All these can serve as authoritative nameservers as well.

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

   "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 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
[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


Past year 2020

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

[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
[*] 2025 - End of Development for Peter J. Philipp after 20 years (planned)


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: 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 (in snapshots) can only do algorithms 7, 8, 10 and 13. Alg 13 support will be released with 1.4 release.

Internal Database holding RR's

New Design

This is somewhat what the new internal database looks like:

|struct rbtree|
        |	+------------+	  		  +------------+
        +-------|struct rrset|--------------------|struct rrset|---> NULL
        |	+------------+	                  +------------+
        |	     |		       		       |
        |	     |		       		       |
        |	+----------+			  +----------+
        |	| struct rr|->struct soa 	  | struct rr|->struct ns
        |	+----------+			  +----------+
        |	     |		       		       |
        |	     v		       		       |
	|	    NULL	     		  +----------+
        |	      		       		  | struct rr|->struct ns
	|	   				  +----------+
	|					       |
	|					       v
	|					      NULL
        |	+------------+	
        +-------|struct rrset|---> NULL
        |	+------------+	
        |	      |		    
        |	      |		   
        |	+----------+
        |	| struct rr|->struct a
        |	+----------+	
        |	      |		   
        |	      v		   
        |	    NULL 
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.


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