Project 2: MX Record Resolver and Mail Router
Build a DNS tool that resolves MX records, applies priority logic, and falls back to A/AAAA when needed.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Intermediate |
| Time Estimate | 1-2 weeks |
| Language | C (Alternatives: Go, Rust, Python) |
| Prerequisites | DNS basics, UDP sockets, byte parsing |
| Key Topics | DNS message format, MX priority, name compression |
1. Learning Objectives
- Construct raw DNS queries for MX and A/AAAA records.
- Parse DNS responses with compression pointers.
- Apply MX priority ordering and fallback rules.
- Resolve hostnames to IPs and present delivery order.
2. Theoretical Foundation
2.1 Core Concepts
- DNS packet format: A 12-byte header followed by question and answer sections with variable-length records.
- Name compression: DNS uses pointers to reuse earlier names, reducing packet size but complicating parsing.
- MX semantics: Multiple MX records specify preference; lower value is higher priority.
- Implicit MX: If no MX exists, RFC 5321 allows delivery to A/AAAA.
2.2 Why This Matters
SMTP delivery begins with DNS. If your resolver misparses or mishandles preference, you deliver to the wrong server or fail entirely.
2.3 Historical Context / Background
DNS predates SMTP authentication. The design optimized for small messages, hence label encoding and compression pointers.
2.4 Common Misconceptions
- Misconception: MX records are always present. Reality: A/AAAA fallback exists.
- Misconception: DNS replies are simple strings. Reality: They are binary packets with compression.
3. Project Specification
3.1 What You Will Build
A CLI tool that accepts a domain or email address, queries DNS for MX records, parses results, resolves each MX hostname to IP addresses, and prints the delivery order with TTL.
3.2 Functional Requirements
- Construct DNS query for MX and send via UDP to a resolver.
- Parse MX answers, including name compression.
- Sort by preference and list results.
- Fallback to A/AAAA if no MX exists.
- Resolve hostnames to IPs.
3.3 Non-Functional Requirements
- Performance: single query under 200 ms on local resolver.
- Reliability: handle truncated responses (TC bit).
- Usability: clear warnings for missing MX or DNS failures.
3.4 Example Usage / Output
$ ./mx-resolver gmail.com
MX records for gmail.com
5 gmail-smtp-in.l.google.com -> 142.250.152.27
10 alt1.gmail-smtp-in.l.google.com -> 142.250.115.26
20 alt2.gmail-smtp-in.l.google.com -> 142.251.9.27
TTL: 300s
3.5 Real World Outcome
You can validate delivery routing for any domain and explain why a server is chosen first, including how fallback behaves when MX records are missing.
4. Solution Architecture
4.1 High-Level Design
CLI
-> DNS Query Builder
-> UDP Transport
-> DNS Parser
-> MX Sorter
-> A/AAAA Resolver
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| Query Builder | Encode header and question | Random ID, recursion desired flag |
| Parser | Decode answers and names | Handle compression pointers |
| Router | Sort MX by preference | Stable sort for equal priorities |
| Resolver | Resolve MX hostnames | Use system resolver or raw queries |
4.3 Data Structures
typedef struct {
uint16_t preference;
char exchange[256];
char ip[64];
} mx_record_t;
4.4 Algorithm Overview
Key Algorithm: Name Decompression
- Read label length byte.
- If top two bits are 11, follow pointer.
- Append labels until zero length terminator.
Complexity Analysis:
- Time: O(n) for n bytes in response
- Space: O(k) for k records
5. Implementation Guide
5.1 Development Environment Setup
cc -Wall -Wextra -o mx-resolver mx_resolver.c
5.2 Project Structure
mx-resolver/
├── mx_resolver.c
├── dns_packet.c
├── dns_packet.h
└── README.md
5.3 The Core Question You’re Answering
“How does a mail server decide where to deliver a message for a domain?”
5.4 Concepts You Must Understand First
Stop and research these before coding:
- DNS Header Fields
- QDCOUNT, ANCOUNT, RCODE
- Flags: QR, AA, TC, RD, RA
- Name Encoding
- Label lengths and terminators
- Compression pointers (0xC0)
- MX Semantics
- Lower preference = higher priority
- Fallback to A/AAAA if no MX
- UDP Behavior
- MTU limits and truncation
- Retries on timeout
5.5 Questions to Guide Your Design
- How will you avoid infinite loops when following compression pointers?
- Will you implement TCP fallback if TC is set?
- How will you cache TTLs and reuse results?
5.6 Thinking Exercise
Given:
MX 10 mx1.example.com
MX 20 mx2.example.com
If mx1 is unreachable, what is the correct next step? What does RFC 5321 say about retry intervals?
5.7 The Interview Questions They’ll Ask
- “How does DNS name compression work?”
- “What happens if a domain has no MX records?”
- “Why use UDP for DNS and when do you use TCP?”
5.8 Hints in Layers
Hint 1: Print raw bytes
- Dump the packet to debug offsets.
Hint 2: Write a recursive name reader
- Track visited offsets to avoid loops.
Hint 3: Validate counts
- Use ANCOUNT to control answer parsing.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| DNS message format | TCP/IP Illustrated Vol 1 | Ch. 11 |
| DNS records | Computer Networks | Ch. 7 |
| Mail routing | RFC 5321 | Section 5 |
5.10 Implementation Phases
Phase 1: Foundation (2-3 days)
Goals:
- Build DNS query packet
- Send via UDP
Tasks:
- Encode header and question.
- Parse header fields in response.
Checkpoint: Receive a valid response to A query.
Phase 2: Core Functionality (4-5 days)
Goals:
- Parse MX answers
- Implement name decompression
Tasks:
- Decode answer section.
- Extract MX preference and exchange.
Checkpoint: Print MX records for a known domain.
Phase 3: Polish and Edge Cases (2-3 days)
Goals:
- A/AAAA fallback
- TTL and caching
Tasks:
- Implement A/AAAA lookup for each MX.
- Add simple TTL cache.
Checkpoint: Handle a domain with no MX.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Compression parsing | Recursive vs iterative | Recursive with offset guards | Simpler to implement |
| Resolver | System resolver vs raw | Raw for MX, system for A/AAAA | Reduces complexity |
| TCP fallback | Implement vs skip | Implement basic retry | More correct for large replies |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Unit Tests | Name decoding | Compression pointer cases |
| Integration Tests | Real domains | gmail.com, example.com |
| Edge Case Tests | No MX | A/AAAA fallback |
6.2 Critical Test Cases
- Compressed MX name: ensure correct decoding.
- No MX: fallback to A/AAAA.
- TC set: retry with TCP or warn.
6.3 Test Data
example.com -> no MX
gmail.com -> multiple MX
7. Common Pitfalls and Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
| Misreading compression | Garbage names | Implement pointer resolution |
| Wrong byte order | Invalid values | Use ntohs/ntohl |
| Ignoring TC bit | Missing answers | Retry with TCP |
7.2 Debugging Strategies
- Compare your packet to
dig +noednsoutput. - Use Wireshark to inspect DNS packets.
7.3 Performance Traps
- Re-resolving the same MX host for each query. Cache results.
8. Extensions and Challenges
8.1 Beginner Extensions
- Add query for AAAA records.
- Accept full email addresses and extract domain.
8.2 Intermediate Extensions
- Implement DNS cache with TTL.
- Add TCP fallback for large responses.
8.3 Advanced Extensions
- Implement full recursive resolver.
- Add DNSSEC validation.
9. Real-World Connections
9.1 Industry Applications
- Mail servers use this logic for routing and delivery.
- Deliverability tools verify MX health and failover.
9.2 Related Open Source Projects
- dig: https://bind9.readthedocs.io/ - reference DNS client
- ldns: https://www.nlnetlabs.nl/projects/ldns/ - DNS library
9.3 Interview Relevance
- DNS parsing and byte-level protocols are standard systems interview topics.
10. Resources
10.1 Essential Reading
- RFC 1035 - DNS protocol details
- RFC 5321 - Mail routing and MX semantics
10.2 Video Resources
- DNS packet parsing walkthroughs
10.3 Tools and Documentation
- dig and nslookup for verification
- Wireshark for packet inspection
10.4 Related Projects in This Series
- Raw SMTP Client for sending logic
- Mini SMTP Server for inbound routing
11. Self-Assessment Checklist
11.1 Understanding
- I can explain DNS header fields
- I can decode compressed DNS names
- I understand MX fallback rules
11.2 Implementation
- MX results are sorted correctly
- Fallback to A/AAAA works
- Errors are clear and actionable
11.3 Growth
- I can debug DNS failures using packets
- I can explain why a server was chosen
12. Submission / Completion Criteria
Minimum Viable Completion:
- Query MX and parse results for a domain
- Print sorted MX list
Full Completion:
- Resolve IPs and fallback to A/AAAA
- Handle compressed names correctly
Excellence (Going Above and Beyond):
- Implement TCP fallback and caching
- Provide JSON output mode
This guide was generated from EMAIL_SYSTEMS_DEEP_DIVE_PROJECTS.md. For the complete learning path, see the parent directory.