**A warm welcome to DNS**
Note: this page is part of the
'[hello-dns](https://powerdns.org/hello-dns/)' documentation effort.
# teaching DNS: Library, Authoritative, Resolver
Welcome to tdns, a 'from scratch' teaching DNS library. Based on `tdns`,
[`tauth`](tauth.md.html) and [`tres`](tres.md.html) implement all of [basic
DNS](../basic.md.html) and large parts of DNSSEC in ~~2000~~ ~~3000~~ 3100
lines of code. Code is
[here](https://github.com/ahupowerdns/hello-dns/tree/master/tdns). To
compile, see the end of this document.
Even though the 'hello-dns' documents describe how basic DNS works, and how
servers should function, nothing quite says how to do things
like actual running code. `tdns` is small enough to read in one sitting and
shows how DNS packets are parsed and generated. `tdns` is currently written
in C++ 2014, and is MIT licensed. Reimplementations in other languages are
highly welcome, as these may be more accessible to other programmers.
Please contact bert.hubert@powerdns.com or
[@PowerDNS_Bert](https://twitter.com/PowerDNS_Bert) if you have plans or
feedback.
The goals of `tdns`, `tauth` & `tres` are:
* Showing the DNS algorithms 'in code'
* Protocol correctness, except where the protocol needs updating
* Suitable for educational purposes
* Display best practices, both in DNS and security
* **Be a living warning for how hard it is to write a nameserver correctly**
Non-goals are:
* Performance
* Implementing more features (unless very educational)
* DNSSEC signing, validation
A more narrative explanation of what `tdns` is and what we hope it will
achieve can be found [here](intro.md.html).
The code for `tdns` can be found on [GitHub](https://github.com/ahupowerdns/hello-dns/blob/master/tdns/) and is also documented
using [Doxygen](codedocs/html).
# Objects in `tdns`
These are found in [dns-storage.hh](https://github.com/ahupowerdns/hello-dns/blob/master/tdns/dns-storage.hh)
and
[dns-storage.cc](https://github.com/ahupowerdns/hello-dns/blob/master/tdns/dns-storage.hh).
## DNSLabel
The most basic object in `tdns` is DNSLabel. `www.powerdns.com` consists of
three labels, `www`, `powerdns` and `com`. DNS is fundamentally case
insensitive (in its own unique way), and so is DNSLabel. So for example:
```
DNSLabel a("www"), b("WWW");
if(a==b) cout << "The same\n";
```
Will print 'the same'.
In DNS a label consists of between 1 and 63 characters, and these characters
can be any 8 bit value, including `0x0`. By making our fundamental data type
`DNSLabel` behave like this, all the rest of `tdns` automatically gets all
of this right.
When DNS labels contain spaces or other non-ascii characters, and a label
needs to be converted for screen display or entry, escaping rules apply. The
only place in a nameserver where these escaping rules should be enabled is
in the parsing or printing of DNS Labels.
The input to a `DNSLabel` is an unescaped binary string. The escaping
example from RFC 4343 thus works like this:
```
DNSLabel dl("Donald E. Eastlake 3rd");
cout << dl << endl; // prints: Donald\032E\.\032Eastlake\0323rd
```
## DNSName
A sequence of DNS Labels makes a DNS name. We store such a sequence as a
`DNSName`. To make this safe, even in the face of embedded dots, spaces and
other things, within `tdns` we make no effort to parse `www.powerdns.com` in
the code. Instead, use this:
```
DNSName sample({"www", "powerdns", "com"});
cout << sample <<"\n"; // prints www.powerdns.com.
sample.pop_back();
cout << sample << ", size: " << sample.size() << sample.size() << '\n';
// prints www.powerdns., size 2
```
Note: for convenience, when parsing human-generated input, `makeDNSName()`
is available to make a DNSName from a string.
Since a `DNSName` consists of `DNSLabel`s, it gets the same escaping. To
again emphasise how we interpret the input as binary, ponder:
```
DNSName test({"powerdns", "com."});
cout << test << endl; // prints: powerdns.com\..
const char zero[]="p\x0werdns";
DNSName test2({std::string(zero, sizeof(zero)-1), "com"});
cout << test2 << endl; // prints: p\000werdns.com.
```
## DNSType, RCode, DNSSection
These is an enums that contains the names and numerical values of the DNS
types and error codes. This means for example that `DNSType::A` corresponds
to 1 and `DNSType::SOA` to 6.
To make life a little bit easier, an operator has been defined which allows
the printing of `DNSTypes` as symbolic names. Sample:
```
DNSType a = DNSType::CNAME;
cout << a << "\n"; // prints: CNAME
a = (DNSType) 6;
cout << a <<" is "<< (int)a << "\n"; // prints: SOA is 6
```
Similar enums are defined for RCodes (response codes, RCode::Nxdomain for
example) and DNS Sections (Question, Answer, Nameserver/Authority,
Additional). These too can be printed.
## `tdig`
To discover how `tdns` works, let's start with the basics: sending DNS
queries and parsing responses. For this purpose, the `tdig` tool is
provided, somewhat modelled after the famous `dig` program created by ISC.
The code:
``` C++ linenumbers
int main(int argc, char** argv)
{
/* ... */
DNSName dn = makeDNSName(argv[1]);
DNSType dt = makeDNSType(argv[2]);
ComboAddress server(argv[3]);
DNSMessageWriter dmw(dn, dt);
dmw.dh.rd = true;
dmw.setEDNS(4000, false);
```
This starts out with the basics: it reads a `DNSName` from the first
argument to `tdns`, a `DNSType` from the second and finally a server IP
address from the third argument.
With this knowledge, in line 8 we create a `DNSMessageWriter` to make a
question for query name `dn` and query type `dt`. In addition, we set the
'recursion desired' flag.
Finally on line 10, we indicate our support for up to 4000 byte responses,
but we set the 'DNSSEC Ok' flag to false.
Next, mechanics:
```
1 Socket sock(server.sin4.sin_family, SOCK_DGRAM);
2 SConnect(sock, server);
3 SWrite(sock, dmw.serialize());
4 string resp = SRecvfrom(sock, 65535, server);
5
6 DNSMessageReader dmr(resp);
```
In line 1 we create a datagram socket appropriate for the protocol of
`server`. This is based on a small set of socket wrappers called
[simplesockets](https://github.com/ahuPowerDNS/simplesocket). On line 2 we
connect and on line 3 we serialize our DNSMessageWriter and send the
resulting packet. On line 4 we receive a response.
Finally on line 6 we parse that response into a `DNSMessageReader`.
```
1 DNSSection rrsection;
2 uint32_t ttl;
3
4 dmr.getQuestion(dn, dt);
5
6 cout<<"Received " << resp.size() << " byte response with RCode ";
7 cout << (RCode)dmr.dh.rcode << ", qname " << dn << ", qtype " << dt << endl;
8 std::unique_ptr< RRGen > rr;
9 while(dmr.getRR(rrsection, dn, dt, ttl, rr)) {
10 cout << dn<< " IN " << dt << " " << ttl << " " << rr->toString() << endl;
11 }
```
On lines 1 and 2 we declare some variable we'll need later to actually
retrieve the resource records. On line 4 we retrieved the name and type we
received an answer for, and on line 6 this all is displayed.
Line 8 declares 'rr' ready to receive our Resource Records, which are then
retrieved using the `getRR` method from the `DNSMessageReader` on line 9.
On line 10 we print what we found. Note that the `RRGen` object helpfully
has a `toString()` method for human friendly output.
# Parsing and generating DNS Messages
This code is in [dnsmessages.cc](https://github.com/ahupowerdns/hello-dns/blob/master/tdns/dnsmessages.cc)
and [dnsmessages.hh](https://github.com/ahupowerdns/hello-dns/blob/master/tdns/dnsmessages.hh).
## `RRGen`s: dealing with all the record types
DNS knows many record types, so we need a unified interface that can pass
all of them. For this purpose, `tdns` uses `RRGen` instances. `RRGen`s are
classes, one for each record type, all deriving from the `RRGen` base.
Each `RRGen` has a method called `toString()` which emits the record's
contents in familiar 'zonefile' format.
`RRGen`s can be created using their specific instance types, for example
like this:
```
ComboAddress ip("203.0.113.1");
auto agen = AGen::make(ip);
cout << agen->toString() << endl; // prints 203.0.113.1
auto soagen = SOAGen::make({"ns1", "powerdns", "com"},
{"bert.hubert", "powerdns", "com"}, 2018102301);
```
`RRGen`s also know how to serialize themselves from a `DNSMessageReader`, or how to
write themselves out to a `DNSMessageWriter`.
When reading DNS Messages (see below), `DNSMessageReader::getRR()` will
return `RRGen` instances to you, if you want to do more than print their
contents, you need to cast them to the specific type, for example:
```
ComboAddress ret;
ret.sin4.sin_family = 0;
if(auto ptr = dynamic_cast