DELPHINUSDNSD    

Index | About | Source | News | Credits | Handbook | Implementation

Design and Implementation of DelphinusDNSD

1. What is DNS? 1.1 Terminology 2. Recursive and Authoritative Nameservers 2.1 Turnkey vs. General Purpose DNS Server 3. Delphinusdnsd 4. DelphinusDNSd timeline 5. Design obstacles 6. Design obstacles with DNSSEC 6.1 DNSSEC errors

1. 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? yahoo.com, google.com, centroid.eu are domain names. Just yahoo, google and centroid are called dns labels, same with com and net. A domain name with the name of "www.google.com" has three labels then where the .com label is also called the Top Level Domain or TLD. Google inside "www.google.com" 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 eu / \ / \ / \ \ google yahoo centroid [1] The (partial) DNS tree showing the root at top. When a domain name lookup is made for "google.com" first the root is consulted for the .com nameservers, then the .com nameservers are consulted for the google.com domain name. It is a distributed database in other words, because the nameservers along the way can belong to different organizations and networks.

1.1 Terminology

DNS - Domain Name System DNS Label - the FOO in FOO.centroid.eu or the WWW in WWW.google.com domain name - www.google.com is a domain name, centroid.eu is a domain name RR - Resource Record, a type of record served by a dns server DNS server - the program that answers to a DNS query A Record - a RR of type A AAAA record - quad-A Record a RR that serves IPv6 answers BIND - Berkeley Internet Name Daemon (a nameserver) UDP - User Datagram Protocol TCP - Transmission Control Protocol (RFC 793) TTL - Time to Live, in DNS a time to live is usually associated with an RR, in IP (Internet Protocol) the TTL is the hop count of a packet TLV - Type, Length and Value LV - Length and Value

2. 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.

2.1 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.

3. 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.

4. DelphinusDNSD timeline

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] [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 Observances: - large gap between 2006 and 2008 for no development. - lots of development around 2010.

5. Design obstacles

a) Compression

A label in a domain name is the FOO in FOO.centroid.eu. It is a LV (length, value) concept where every label is prepended with the length of the actual label. So FOO.centroid.eu would look like 3FOO 8centroid 2eu 0. The domain name is terminated with a length of 0. When there is a DNS packet with several domain names then compression can be used. Since there is up to 63 characters in a label, the label length itself occupies the low 6 bits of the 8 bit ASCII character. The 2 high bits (0xC0) when set indicate a compression pointer otherwise they must be set to zero. 8 7 6 5 4 3 2 1 bit +----+----+----+----+----+----+----+----+ |0x80|0x40|0x20|0x10|0x8 |0x4 |0x2 |0x1 | value +----+----+----+----+----+----+----+----+ | 0xC0 | 0-63 (0x3F) | combined +---------+-----------------------------+ So when there is a domain name that matches fully or partially to a domain name that is being added to a DNS packet you can compress it. A compression pointer is by property 16 bits long and thus can hold 14 bits of offset from the beginning of the packet so 16384 bytes. This means a domain name can be compressed to a previous domain name that resides in the beginning 16384 bytes of a DNS packet this is somewhat a limitation on 65535 maximum length TCP DNS packets. In Delphinusdnsd the compression was rewritten on April 12th, 2009 when W was in BETA_5 version. Previous versions of compression was less efficient but packed more. They were also buggy so that they prompted rewrite. This is hard code and can break heads. The way Delphinusdnsd adds compression is to append a name to the end of a dns packet (buffer) and then parses the first 100 resource records and their respective names: for (i = 1; i < 100; i++) { in an array it records the offset of the label. Then when it runs a second time in a loop it does a memcasecmp() which is a memory compare that knows a bit about ASCII characters and their respective cases. if (memcasecmp(e, p, checklen) == 0) { What happens then the offset is stored at the end of the buffer where the dns name first resided and a 0xC0 is added. The buffer offset is then trimmed to only show the size of the compression pointer.

b) AXFR

AXFR is a DNS Zone Transfer. There is 2 types. AXFR and IXFR. AXFR is a full zone transfer and IXFR is an incremental transfer. Although both types are supported in Delphinusdnsd, a zone is always transferred as a whole and never increment. AXFR was added September 19th, 2011. IXFR followed a few days later as it was simple to do with full dumps. A delphinusdnsd slave will not use AXFR but rather the zones are better synced with rsync over SSH. A manual SIGHUP of the master daemon will reload the config files and sync the zone across a homogenous delphinusdnsd dns setup.

c) ANY RR answers

The ANY RR lists all RR's found for a certain domain name. If at the root of a "zone" this may contain an SOA, NS, A and AAAA records, but any type will do really. Delphinusdnsd attained ANY query support through a special request by Alexander Dzuba, on December 27th, 2010. Seasons Greetings! The functions that were written for the ANY requests were usable at a later time for the AXFR code where basically the BTREE is walked and the fitting domain names are written to a packet with the ANY method. It's possible that a DNS reply gets the truncation flag and gets truncated when spouting ANY RR's.

d) Recursion

I tried recursion a few years ago. It's really hard to implement so I decided to stay with an authoritative nameserver for the time being.

e) Round-Robin DNS

Appeared in Delphinusdnsd on November 3rd, 2009. It is a simple integer in the zone that gets updated with every lookup. With a MOD determination the A, AAAA, and NS records are rotated. This was prodded by Evgeniy Bogdanov who gave incredible input back.

f) Split-Horizon DNS

Was implemented for A RR records (IPv4) on March 27th, 2010. It's for IPv4 functionality but lookups through IPv6 can also be weighed. This feature requires a homogenous delphinusdnsd dns setup and is pretty useless if you want to slave with BIND. This feature has been turned off in delphinusdnsd version 1.0.0 due to complications with DNSSEC.

g) Wildcarding

Delphinusdnsd has disabled wildcarding in version 1.0.0. It took 10 years.

h) Truncation

Has been fixed in delphinusdnsd-1.0.0, before it would truncate at the wrong spot. The fix also honors different sizes as negotiated with EDNS. This was revisited in 2017 with delphinusdnsd-1.2.0 and truncated answers now give an "empty answer" back. This prevents complications with wrong record counts.

i) TCP

TCP support came to Delphinusdnsd on March 12th, 2010, so relatively late. TCP support is a needed function in DNS and MUST be supported according to many DNS related RFC's. Unfortunately when a dns packet gets truncated in UDP it will also get truncated in TCP even though TCP supports a packet length of up to 65535 characters. This still needs work. The AXFR function which is solely TCP is seperated from the main dns code so that there is no possibility for a denial of service while a zone transfer takes place.

j) IPv6

IPv6 support has been in delphinusdnsd from day one.

k) SRV RR

SRV support came in April 2012. It is based heavily on the MX RR and has not been tested as much as the author would like. Feedback is always welcome.

6. Design obstacles with DNSSEC

DNSSEC been 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. Also reply_nxdomain had to be modified to give NSEC3 answers. The way these DNSSEC RR's make it into the daemon 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 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.

6.1 DNSSEC errors

In 2016 some DNSSEC errors were detected in the Implementation: a) RRSIG's should be multiple RRSIG's per RR which it is currently not. This is needed for key rollover which the current version of delphinusdnsd does not do. b) key rollover is not supported as hinted on in a), it is a little like the root zone DNSKEY which seldomly gets rotated in our case, never. Subsequent versions (perhaps 1.1.0) will support key rollover.