How rrchelbi/apex works
1. Top-level shape
- This is a single Rust package with both a library core (src/lib.rs) and a binary entry point (src/main.rs).
- The architecture is intentionally small: one network-facing server module, one protocol module tree, and a thin executable wrapper.
2. Execution path
- main.rs boots the process, configures logging, and starts the DNS server.
- The server binds to UDP 0.0.0.0:2053 by default, so the whole system is centered around raw DNS datagrams rather than HTTP or a higher-level framework.
3. Transport boundary
- src/server.rs is the runtime boundary between the network and the resolver logic.
- It receives UDP packets, hands them to the parser, drives resolution, then serializes the reply back onto the wire.
- This module is where request/response lifecycle, socket handling, and logging of queries live.
4. Protocol subsystem
- src/protocol/ contains the DNS wire-format implementation.
- header.rs, packet.rs, question.rs, query.rs, and record.rs model RFC 1035 concepts directly, with a focus on zero-copy parsing/serialization from raw bytes.
- This layer isolates all DNS-specific byte layout concerns from the server logic.
5. Internal data model
- Incoming wire bytes are converted into structured DNS types such as headers, questions, queries, and records.
- The resolver operates on these internal types, not on raw buffers, which keeps packet handling and resolution logic cleanly separated.
6. Recursive resolution flow
- The server performs recursive DNS resolution itself, rather than delegating to the host OS resolver.
- The flow starts from root servers, then iteratively follows referrals to authoritative name servers until it can answer the question.
- The debug trace in the README shows this as a series of lookups against upstream DNS servers.
7. Record handling scope
- The project currently supports A, AAAA, NS, CNAME, and MX records, plus unknown record passthrough.
- That means the protocol layer and resolver are designed to preserve and forward DNS data even when it is not fully understood.
8. Cross-cutting concerns
- anyhow is used for error propagation across parser, resolver, and server boundaries.
- tracing and tracing-subscriber provide structured logs, which are important for following recursive resolution steps and debugging wire-level failures.
9. Current architecture limits
- The README makes clear this is still early-stage: no TTL cache, no concurrent query handling, no authoritative zone service, and no DoT/DoH.
- Architecturally, that means the current system is a recursive UDP resolver, not a full DNS platform.
10. Diagram-friendly boundary summary
- A useful architecture diagram would show:
- Client DNS query → UDP listener/server → protocol parser → recursive resolver → upstream root/authoritative servers → protocol serializer → UDP response
- The key internal split is between transport + orchestration in server.rs and wire-format DNS modeling in src/protocol/.
Main entry points: server.rs DNS server · packet.rs Packet · lib.rs Library core · main.rs Main
Components 11
Runtime
Process entry, UDP server, and recursive DNS flow.
- DNS client · Sends DNS queries over UDP.
- Main · Boots logging and starts the server.
src/main.rs - Library core · Shared library surface for the resolver and protocol modules.
src/lib.rs - DNS server · Receives packets, drives recursive resolution, and writes replies.
src/server.rs - Root servers · Starting point for iterative recursive lookup.
- Auth servers · Authoritative name servers followed during referrals.
Protocol
DNS wire-format parsing and serialization.
- Header · Models the DNS header fields in wire format.
src/protocol/header.rs - Packet · Top-level DNS message parsing and serialization.
src/protocol/packet.rs - Question · Represents the query question section.
src/protocol/question.rs - Query · Structured internal query model used by resolution logic.
src/protocol/query.rs - Record · Models resource records, including unknown passthrough.
src/protocol/record.rs
Connections 14
- DNS client → Main: starts · Client traffic reaches the process started by the binary.
- Main → DNS server: launches · Main configures logging and starts the UDP DNS server.
- Library core → DNS server: exports · Library crate exposes the server runtime.
- Library core → Packet: exports · Library crate exposes protocol primitives.
- DNS server → Packet: parses · Incoming UDP datagrams are decoded into DNS packets.
- Packet → Header: contains · Packet structure includes DNS header fields.
- Packet → Question: contains · Packet structure includes question sections.
- Packet → Record: contains · Packet structure includes resource records.
- Question → Query: maps to · Parsed questions become internal query objects.
- DNS server → Query: drives · Server resolves structured queries instead of raw bytes.
- DNS server → Root servers: queries · Recursive resolution begins at root servers.
- Root servers → Auth servers: refers · Referrals are followed to authoritative servers.
- Auth servers → Record: returns · Authoritative answers are represented as DNS records.
- DNS server → Packet: serializes · Resolved answers are encoded back onto the wire.