This project implements a custom DNS (Domain Name System) server in Rust, capable of handling DNS queries and providing responses. It features a robust architecture for decoding DNS packets, resolving domain names using an upstream resolver (defaulting to Google's 8.8.8.8), and constructing efficient DNS responses.
- Custom DNS Protocol Implementation: Handles UDP-based DNS queries and constructs compliant DNS responses.
- Upstream DNS Resolution: Forwards queries to a configurable upstream DNS resolver (e.g., 8.8.8.8) to resolve domain names.
- Efficient DNS Response Builder: Utilizes a custom
DnsResponseBuilderfor creating DNS response packets with:- Zero-clone response creation when taking ownership of query packets.
- Copy semantics for DNS headers, minimizing heap allocation.
- Reusable builder instance for multiple requests.
- Support for various DNS record types (A, AAAA, CNAME, MX, TXT, etc.).
- Fluent interface for readable and chainable method calls.
- Error Handling: Robust error handling for decoding and encoding DNS packets, and for upstream resolution failures.
- Structured Logging: Integrates
tracingfor detailed logging of server operations, packet details, and errors. - Command Line Interface (CLI): Configurable upstream resolver via command-line arguments.
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
- Rust programming language (version 1.80 or newer recommended)
- You can install Rust using
rustup:curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
- You can install Rust using
-
Clone the repository:
git clone https://github.com/your-username/codecrafters-dns-server.git cd codecrafters-dns-server -
Build the project:
cargo build --release
The DNS server listens on 0.0.0.0:2053 by default.
cargo run --releaseTo specify a different upstream resolver (e.g., Cloudflare's 1.1.1.1):
cargo run --release -- --resolver 1.1.1.1:53You can test the server using dig or nslookup.
Using dig:
dig @127.0.0.1 -p 2053 example.comUsing nslookup:
nslookup example.com 127.0.0.1 -port=2053The project is organized into several modules within the src/ directory:
src/main.rs: The main entry point of the application, responsible for setting up the UDP socket, initializing the DNS resolver, and handling incoming DNS queries.src/cli.rs: Handles command-line argument parsing usingclap.src/codec.rs: Implements theDnsCodecfor encoding and decoding DNS packets usingtokio-util::codec.src/errors.rs: Defines custom error types for the application.src/parsers.rs: Contains parsing logic for DNS packet components.src/protocol.rs: Defines the data structures for DNS protocol elements (headers, questions, records).src/response_builder.rs: Implements theDnsResponseBuilderfor constructing DNS responses.src/actors/: Contains actor-based components (e.g.,set_id_actor.rs,messages.rs).src/handlers/: Contains handlers for specific DNS operations (e.g.,set_id_handler.rs).
Key dependencies are managed via Cargo.toml:
anyhow: For flexible error handling.bytes: Utilities for byte buffers.clap: For parsing command-line arguments.futures: Asynchronous stream utilities.hickory-resolver: A DNS resolver library used for upstream lookups.nom: A parser combinator library for robust parsing.thiserror: For declarative error types.tokio: An asynchronous runtime for building network applications.tokio-util: Utilities for Tokio, including codecs.tracing: For structured logging and diagnostics.tracing-subscriber: A subscriber fortracingevents.
To run the comprehensive test suite for the project:
cargo testSpecifically for the DnsResponseBuilder tests:
cargo test response_builderContributions are welcome! Please feel free to submit a pull request or open an issue.
This project is licensed under the MIT License - see the LICENSE file for details.