Here are a bunch of ways to extend this toy DNS resolver. All of these are features that real DNS resolvers have. They’re not in any particular order – do whichever one(s) seem the most fun to you, or none of them.
1. make it work with
Some domain don’t have an
A record: instead they have a
CNAME record redirecting to another domain name. For example
www.facebook.com is like this. Modify the code so that it works with
Here’s an example of the code failing:
from part_3 import resolve TYPE_A = 1
Querying 220.127.116.11 for www.facebook.com Querying 18.104.22.168 for www.facebook.com Querying 22.214.171.124 for www.facebook.com
--------------------------------------------------------------------------- Exception Traceback (most recent call last) Cell In , line 1 ----> 1 resolve("www.facebook.com", TYPE_A) File ~/work/tinydns/book/part_3.py:105, in resolve(domain_name, record_type) 103 nameserver = resolve(ns_domain, TYPE_A) 104 else: --> 105 raise Exception('something went wrong') Exception: something went wrong
2. support other record types by name¶
Right now to query for an A record, you need to pass the number of the “A” record type (
TYPE_A = 1). It would be way more usable if you could pass in a string like
"A" and have it translated to the correct number. There are some mappings in section 3.2.2 of the RFC, as well as some newer ones you might need hunt down that were invented after 1987.
Also the way you parse the contents (the
data) of a DNS record depends on the type, so you could implement parsing for NS records, AAAA records, TXT records, etc.
3. don’t allow loops in DNS compression¶
A malicious actor could exploit our DNS compression code by sending a DNS response with a DNS compression entry that points to itself, so that read_domain_name would end up in an infinite loop. Fix it to avoid that attack.
For example, here’s the code that avoids loops in miekg/dns
4. cache DNS records¶
Real DNS resolvers implement caching, so that if you make a second query 1 second later, it doesn’t need to go make a million DNS queries to figure out the answer.
One thing to keep in mind here is that DNS is case insensitive.
5. implement EDNS0 (extended DNS)¶
You might have noticed that in section 3.4, the
additionals section we got from the root nameserver was truncated. If you run
dig +all @a.root-servers.net example.com, you’ll get a different list.
This is because in the original DNS spec, response sizes were limited to 512 bytes. But if you implement EDNS0, you can get a larger response.
One way you could approach this is by using tcpdump or Wireshark to look at the DNS request
dig is sending, and the mimicking what it does. The specification is RFC 2671 (though personally I find the spec to be a pretty confusing read, I’d start by mimicking
You can test whether this works by running
send_query("126.96.36.199", "google.com", TYPE_A) and checking if the total size of the response you get is more than 512 bytes. You can also use the DNS Reply Size Test Server to test.
6. implement TCP DNS¶
To get really big DNS responses, you can implement DNS over TCP. This is mostly the same as DNS over UDP, you just open a TCP socket instead. You can run
dig +tcp example.com if you want to capture some TCP DNS traffic with Wireshark to see what it should look like. The length field is handled differently than it is with UDP, see the RFC for more.
I’m not sure how to actually find a gigantic 50KB DNS response to test your TCP DNS implementation out on though, and I’m too lazy right now to figure out how to create a few hundred DNS records to make one. Let me know if you find one.
7. make your resolver into a DNS server¶
Instead of running your toy resolver as a command line program, you can run it as a UDP server and listen on port 5353 or something. You can test it like this:
dig @127.0.0.1 -p 5353 example.com
If you’re feeling very brave (this is not a good idea!!), you could even configure your personal computer to use it a resolver and see what breaks. Just make sure you know how to change it back, and probably don’t keep it that way for too long. If you haven’t implemented caching, it’ll make your DNS queries slower than they should be and also put some unnecessary pressure on the authoritative DNS servers you’re querying. In general caching is very important to the health of the overall DNS system :).