A toy DNS server for learning about the Domain Name System and high performance networking in Rust.
If you are looking for a non-toy DNS server written in Rust, check out trust-dns.
cargo run
cargo run &
dig @127.0.0.1 -p 8080 +noedns google.com
netcat -ul 5300 > query.bin &
dig @127.0.0.1 -p 5300 example.com
# kill netcat
cat query.bin | nc -u 1.1.1.1 53 | tee response.bin
Disclaimer: This is a personal project, I am not a security expert and make no guarantee of security.
The history of DNS security is a bit of a mess, I've made a best effort to navigate it and implement mitigations for known issues.
With only a 16 bit transation ID and no cryptographic verification, DNS over UDP is vulnerable to an attacker injecting malicious responses. If no IP verification is done and the source port can be guessed, an attacker can simply send a query as normal and then inject a few thousand malicious response packets with guessed transaction IDs until a collision happens. See also [1] [2].
Mitigation:
- UDP source port is randomized for each query, this adds 14-16 bits of entropy
- Transaction ID is randomized for each query using a cryptographic generator
- Responses must originate from the IP address the query was sent to
If multiple clients share a caching DNS server, they can both time responses for a domain and look at the returned TTL values to determine if a domain was previously queried and if so how long ago it was cached.
Mitigation:
- Optionally zero out TTL values on responses
- Ignore non-recursive queries to caching server
Mitigation:
- Separated DNS server and recursive resolver functionality (DNS servers and recursive resolvers should never run on the same IP address)
- No TCP support for now, it is relatively easy to cache poison because only the first packet in multi-packet responses include the transaction ID
- Only IN and ANY question classes are allowed for implementation simplicity
A set of security extensions introduced in 1997 that have failed to reach wide acceptance and have a number of critisims. Implementation would add substantial complexity. I am more interested in pursuing modern alternatives like DNSCrypt, DNS over TLS, or DNS over HTTPS.
- Apply one week TTL limit to all responses
- Cache negative responses
- Truncate to-long responses (and set TC bit)
- Support for extended UDP responses
- Response compression & better packet cursor - https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
- Fuzz testing
- Benchmarking
- Initialize from from resolver.conf etc
- Increase incoming UDP socket buffer size (similar to socket_tryreservein, something like 128KB is enough)
- Recursive resolver (note http://cr.yp.to/djbdns/separation.html, though I never intend this to be an authoritative server)
- DNSCrypt, DNS over TLS, or DNS over HTTPS support
- TODO: Use cryptographic generator to select ports instead of relying on Linux's behavior of finding an open port when binding to port zero. There are two main issues with Linux's behavior. First, older kernels have a "trivially predictable" prandom_u32 implementation used to select a port. Newer kernels utilize SipHash which is a "PITA" to guess, but still not cryptographically secure. Second, Linux selects from a relatively small set of source ports. Specifically it prefers to use odd ports for outgoing connections, and only from the configured ephemeral port range (net.ipv4.ip_local_port_range) which is usually 32768-60999. That's effectively one quarter of the available ports, meaning instead of 16 bits of security against cache poisoning we get under 14 bits, a meaningful difference.
Inspired by https://github.com/EmilHernvall/dnsguide.
Written with reference to:
- http://cr.yp.to/djbdns.html
- https://wizardzines.com/zines/dns/
- RFC 1123 - Requirements for Internet Hosts - SUPPORT SERVICES - DOMAIN NAME TRANSLATION
- RFC 1034 - DOMAIN NAMES - CONCEPTS AND FACILITIES
- RFC 1035 - DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
- RFC 2181 - Clarifications to the DNS Specification
- RFC 4343 - Domain Name System (DNS) Case Insensitivity Clarification
- RFC 6891 - Extension Mechanisms for DNS (EDNS(0))