Domain Foundations – The first of our five year vision

Domain Foundations – The first of our five year vision
Photo by Stefan Kunze / Unsplash

By Arya Khanna, Martin Hoffmann and Alex Band

About a year ago, we unveiled our vision for the next five years of DNS at NLnet Labs. In this series of articles, we’d like to provide you with an update on our accomplishments last year and our plans for 2025 and beyond. 

Although our long-standing projects underwent several notable updates, including the implementation of DNS-over-QUIC support in Unbound and the introduction of a SIMD-capable zonefile parser in NSD, this initial article will primarily focus on the development work we’ve undertaken on our domain library for DNS. 

The goal of domain is to provide a comprehensive—ideally complete—set of building blocks for constructing specialized DNS applications or integrating DNS into other applications. The library is written in Rust, a programming language that fosters a thriving developer community with its robust type system and ownership model, ensuring memory and thread safety.  Throughout 2024, the Sovereign Tech Agency has actively supported the project’s development.

At the beginning of last year, the library, or crate in Rust terminology, offered several fundamental features that focused on representing the components of a DNS message, such as domain names and resource records. To effectively build upon this foundation, we organized our work into four parallel tracks: client capabilities, server capabilities, proxy capabilities, and finally, tooling.

When we committed to this project with Sovereign Tech Agency, we set some very ambitious goals. The project encompassed ten major milestones, initially planned to be handled by three developers: Martin, Philip, and Ximon. Over the course of 2024, we expanded the team to include Arya, Jannik, and Terts, allowing us to broaden the original scope. All development was conducted openly, with public pull requests (PRs) and reviews, ensuring complete transparency throughout the process. 

🙌
Although the domain crate is no longer directly funded, the six developers who now work on the project will remain dedicated to its development in 2025 and beyond. We encourage everyone interested in secure and private name resolution to support our independent, self-sustaining foundation.

Client Capabilities

As part of the milestones, we worked towards strengthening domain’s DNS client functionality. While it could already make and send DNS requests, we introduced a whole new client architecture and used it to implement important DNS extensions.

PR 215 introduced this new architecture, breaking down DNS client functionality into many composable layers. Each layer implements a single feature (e.g. caching requests, adding TSIG protection, or communicating over UDP). Users can pick the layers they want and build a custom pipeline for answering DNS requests. This offers a lot of flexibility, and it allows us to incrementally develop new features by introducing new layers. This PR also overhauled the existing stub resolver – which helps applications find the right IP address for a domain – to take advantage of this layered architecture.

PR 275 added a new caching layer to this architecture. Applications sometimes make the same DNS requests in quick succession: this layer speeds them up by remembering recent request-response pairs and returning those saved responses. This avoids unnecessarily requesting information from a DNS server over the network.

PR 328 implemented a complete DNSSEC validator, which inspects responses to queries and ensures they are legitimate (i.e. are not being forged by an attacker). It is implemented as a new client layer, so that validation can be added to existing pipelines transparently. While it is functionally complete (supporting NSEC, NSEC3, wildcards and CNAME/DNAME chains), we hope to improve its performance and help users select DNSSEC trust anchors automatically.

PR 373 added a TSIG layer, allowing clients to sign their requests and prevent modifications by an attacker. To use TSIG, clients and servers need to share a secret key – it can’t be used for regular DNS requests to public servers. Instead, its audience is name servers which need to communicate zone transfers to each other.

PR 375 and PR 376 implemented the basic logic of zone transfers (XFRs). Rather than fiddling with the minute details of XFR responses, clients simply get an updated zone to serve. This is important for secondary name servers which need to be synchronized to an upstream source.

Server Capabilities

In 2024, we added DNS server support to domain. While there’s still some important features to implement, we’re proud of the breadth of functionality domain now offers.

PR 274 and PR 307 introduced a layered service architecture, inspired by the client-side architecture of PR 215.  Requests can be received over UDP and TCP. Important middleware services, like EDNS cookie management, are already implemented. Users can pick the layers they want, put them in a pipeline, and get a functional DNS server.

A name server’s basic responsibility is to answer DNS queries from a local database. To that end, PR 286 implemented an in-memory storage for zones and trees of zones. A DNS zone can be loaded from a zonefile (using domain’s existing support for zonefile parsing) and DNS requests can be answered by traversing the zone. The zone storage can be provided by a custom backend (e.g. a MySQL server) so that users can manage their zone data as they wish.

PR 384 implemented most of the logic of serving zone transfers (XFRs). It supports both authoritative and incremental transfers (AXFRs and IXFRs, respectively). Incremental transfers rely on the zone tree’s ability to track multiple versions of a zone at once. PR 383 breaks down zone transfers into multiple DNS response messages that can actually be sent back to clients.

PR 380 matched client-side support with server-side support for TSIG. PR 382 implemented NOTIFY requests, allowing a name server to inform downstream servers that the content of a zone has changed.

While the zone tree is fairly complete, it is crucially lacking DNSSEC support. We’re also looking to improve its memory and performance characteristics.

👷‍♀️
When building new functionality that touches the existing code base, of course you identify areas that can be improved. The domain crate is organized as a number of high-level modules providing entire categories of functionality, like client-side networking or zonefile parsing; all of these modules rely on the foundations laid by domain::base. Over time, we've found ways to improve its ergonomics, and we're in the midst of reworking it to integrate those changes. This is a major change; we'll cover the details in an upcoming article, and you can track the progress in PR 474.

DNSSEC Signing

In addition to the client-side validator, we began working on the server-side considerations of DNS security. DNS signers have to store cryptographic keys, can rotate them, and use them to sign zones.

PR 406 added a representation for cryptographic keys. They can be generated, stored in memory, and loaded from / saved to disk. Multiple backends are supported (currently ring and OpenSSL), and users can introduce custom backends if necessary.

PR 459 added lifecycle management for DNSSEC keys. Keys go through several states as they are introduced, become available downstream (in name servers and resolvers), get used for signing zone content, and finally become replaced by newer keys. The lifecycle manager determines which keys are appropriate for signing a zone, allowing keys to be safely rotated over time.

While domain had some existing support for zone signing, it was incomplete and very limited. PR 416 overhauled it to a mostly feature-complete status, using PR 406 to handle keys and introducing support for NSEC3 and signing a single zone with multiple keys. A major performance hurdle for zone signing is the need to sort records (in order to generate NSEC / NSEC3 records): PR 447 parallelized this step to yield major speedups.

Proxy Capabilities

In 2024, we introduced mimir, a DNS proxy and load balancer.  Users can dynamically pick between recursive resolvers or authoritative servers, based on the queried information (e.g. all requests for .com domains go to one resolver, and everything else goes to another) or for load balancing.

Mimir is built on domain and demonstrates its flexibility and usefulness.  The core functionality was implemented in domain by PR 353: it added a query name router, forwarding requests to different destinations based on the queried domain name.  If mimir is not powerful enough, users can always work with domain directly.

As part of this development, we discovered potential use cases for proxies that warranted standardization.  Philip submitted two IETF drafts,  “Control Options For DNS Client Proxies” and “Implementation Guidelines for Authoritative DNS Proxies”.  While these drafts were discussed in mailing lists and meetings, they can’t be implemented yet.

Tooling

In 2024, we introduced dnsi, a utility that can interact with DNS servers.  It aims to provide the same functionality (and more!) as dig, but with a more ergonomic command-line interface.  As another tool built on domain, it shows the benefits of our one-size-fits-all codebase.  Novel functionality over dig includes the ability to compare two name server’s responses to the same query, to check if one is lagging behind.

Using domain, we’ve worked to introduce replacements for the ldns utilities.  These utilities were originally just examples showcasing the use of the ldns library, but they became unexpectedly popular.  As simple example programs, they had unergonomic interfaces and subtle edge cases that made them difficult to use.  We’ve begun porting them over to Rust and domain in a new repository, dnst.  At present, six utilities have been implemented: key2ds, keygen, nsec3hash, signzone, notify, and update.  Each utility is exposed as a compatibility interface (functioning as a drop-in replacement for the ldns equivalents), and with brand-new and more ergonomic interfaces.  We’re still working on porting more utilities, and eventually packaging and distributing these replacements.

An Epic Adventure

2024 has been a pivotal year for NLnet Labs. Our vision for DNS, our commitment to the Rust programming language and, in its wake, the influx of new talent joining our team, all combined with the support we received from Sovereign Tech Agency, we’ve been able to make a giant leap forward in realizing our goals.

🙏
Throughout our journey, collaborating with the team at Sovereign Tech Agency has been an absolute delight. We are incredibly grateful for their unwavering support and visionary approach to supporting open-source software. 

We set out to make 2024 a catalyst for the development of the domain and all the projects that will emerge from it in the years to come. We are thrilled to have achieved this goal and are eager to maintain the momentum. It is incredibly encouraging to witness the growing adoption of the domain crate and the valuable contributions we have received thus far.

If you are an existing user of domain or have plans to deploy it, we encourage you to share your feedback on the functionality, API, documentation, or any other aspect of the project. Please don’t hesitate to open an issue on GitHub or start a discussion. We are excited to continue on this journey together with you!