21 comments

[ 3.7 ms ] story [ 48.3 ms ] thread
This is a fantastic and well-written guide, thank you!

I recently wrote a toy caching DNS proxy in Elixir, and I have a question which I’ve never been able to figure out. Individual DNS records for the same hostname can have different TTLs configured. For example, 30s for one record and 300s for another. As a caching resolver, what is the expected behaviour when the record with the shorter TTL has expired but the other has not? I chose to invalidate the entire thing and make a new query upstream, but I’ve always wondered what the “proper” behaviour should be.

Do you have a link to your toy dns proxy?
Having multiple TTLs in the same record set is deprecated. [1]

If you would rather put them in your cache, instead of not allowing to request them through your server, you are probably best off, by taking the lower TTL and using that as your initial TTL. E.g. this is how an authoritative Knot instance would handle differing TTLs in a DNSSEC signed zone.

[1] https://tools.ietf.org/html/rfc2181#section-5.2

I haven't written a dns server so this might be stupid question, but why would differing TTLs be a problem? You'd need just to key your cache with (label, recordtype) tuple, right?
From the RFC link in a sibling comment[1]:

> No uses for this have been found that cannot be better accomplished in other ways. This can, however, cause partial replies (not marked "truncated") from a caching server, where the TTLs for some but not all the RRs in the RRSet have expired.

So the cache could be split up like that, but then it could produce partial results, which is worse than reducing the effective TTL.

[1] https://tools.ietf.org/html/rfc2181#section-5.2

Sometimes it‘s like somebody is reading my brain. I was just considering to have a look into DNS to solve a particular problem with software-licensing / enforcement. It‘s still likely a wrong idea and not well thought out, but I‘m even closer to actually waste resources on it, now :-)
I’m the author of this. Fun to see it appear here a few years after I wrote it. I originally put this together in an attempt to fish for a Rust gig. It worked out! I current coworker of mine saw it in /r/rust and passed my name to their recruiter.

I’ve felt at times that this project is a bit dated in that it uses synchronous io. I’ve changed my mind about that though. It’s more about teaching DNS than about teaching rust, and async is complex enough that it’d be a pedagogical impedement. Synchronous rust, on the other hand, is very simple to grasp.

The first thing I check in DNS software is whether there is a trivial denial of service attack in the name decompression code. Sadly this code fails :-(

It is vital that you have a limit on the number of label pointers that you follow, either by ensuring the pointer points to an earlier point in the packet, or by limiting the number of indirections.

Yeah, it was pointed out by someone on /r/rust when I first posted it. I thought I had corrected that, but apparently not. I’ll revise that tonight. It’s obviously a bad mistake.
DNS name (de)compression is super tricky and even experts get it wrong in production code, so it's a forgivable mistake :-)

It's an interesting trap/pitfall, though, because it's an example of a situation where basic memory safety isn't enough. I would love to see some examples of how to use Rust's type system to protect against DNS name compression loops, e.g. using a slice of the packet to force compression pointers to go backwards.

Took me a few days before I could get to it, but this has been fixed now.
Why would someone need to write their own instead of just running bind?
Learning how it works
I built PendingDNS (https://github.com/postalsys/pending-dns) for a service I run mostly for two reasons:

1) no need to mess with zone files, all data is written to and read from Redis, so it is much easier to integrate

2) changes are always immediate, no need to reload server daemon, which means I can generate DNS based Lets Encrypt wildcard certificates really fast as there is no need to wait for “propagation”

We're geeks and this is HN. "Need" is irrelevant and has nothing to do with it.
Monocultures are bad, for one. A diversity of implementations drives improvements. For example, at one time one could have said "...instead of just running sendmail?" for SMTP servers. But the emergence of things like exim, qmail and especially postfix resulted in better servers.