Implement a Simplified DNS Resolver
AnthropicMachine Learning Engineer · Online AssessmentLast updated
Implement an in-memory DNS resolver supporting A and CNAME record lookups, domain name normalization, TTL expiration, and CNAME alias chain cycle detection.
Problem Description
Implement an in-memory DNS resolver that manages resource records and handles recursive lookup semantics for domain queries.
Your resolver must support two record types:
"A": Maps a domain name to an IPv4 address string (e.g.,"192.0.2.1"). MultipleArecords can exist for the same domain."CNAME": Maps an alias domain name to a canonical domain name (e.g.,"target.example.com"). A domain can have at most one validCNAMErecord.
Requirements
- Domain Normalization: Domain names are case-insensitive and may optionally end with a trailing dot (
.). For example,"example.COM."and"example.com"refer to the same domain. - TTL Expiration: Records are inserted with a
ttl(Time To Live, in seconds) at a specifiedcurrent_time. A record is valid for queries wherecurrent_time < insertion_time + ttl. If a record has expired bycurrent_time, it must be ignored during resolution. - CNAME Resolution: When querying for record type
"A"on a domain that has a validCNAMErecord pointing to another domain, the resolver must follow the CNAME chain until it finds validArecord(s) for the target domain. - Cycle & Hop Detection: If a CNAME resolution encounters a cycle (e.g.,
a.com -> b.com -> a.com) or exceeds 10 hops, resolution must fail immediately and return an empty list[]. - Record Conflicts: If a domain already has a valid
CNAMErecord, adding anArecord should overwrite/invalidate theCNAMErecord, and vice versa.
Class Interface
class DNSResolver:
def __init__(self):
pass
def add_record(self, name: str, record_type: str, value: str, ttl: int, current_time: int) -> None:
"""
Stores a DNS record.
"""
pass
def resolve(self, name: str, record_type: str, current_time: int) -> list[str]:
"""
Resolves the query at current_time.
Returns a sorted list of unique values (e.g., IP addresses), or an empty list if not found or on cycle/exceeded hops.
"""
pass
Examples
resolver = DNSResolver()
resolver.add_record("example.com", "A", "93.184.216.34", ttl=60, current_time=100)
resolver.add_record("www.example.com.", "CNAME", "example.com", ttl=60, current_time=100)
# Direct lookup
resolver.resolve("EXAMPLE.COM.", "A", current_time=120)
# Returns: ["93.184.216.34"]
# CNAME resolution
resolver.resolve("www.example.com", "A", current_time=150)
# Returns: ["93.184.216.34"]
# Expired TTL
resolver.resolve("www.example.com", "A", current_time=170)
# Returns: []
Constraints
- Number of operations (
add_record+resolve) = 1. current_timeis non-decreasing across consecutive queries.- Max hops for CNAME resolution: 10.
Hints
Hint 1
Normalize domain names immediately upon input by lowercasing and stripping any trailing period.
Hint 2
Store records per domain using a data structure that tracks expiration time `(insertion_time + ttl)` alongside the values, separating or enforcing mutual exclusion between A and CNAME types.
Hint 3
When resolving A records via CNAMEs, keep a visited set and a hop counter to abort if a cycle is found or the hop count exceeds 10.
Follow-up questions
- How would you ensure thread safety if `add_record` and `resolve` were called concurrently across multiple threads?
- If memory were strictly bounded, how would you implement proactive eviction of expired records rather than relying purely on lazy evaluation?
- How would you modify the resolver to support negative caching (caching NXDOMAIN responses to avoid repeated failed queries)?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.