Core Question
Core Question
NXDOMAIN, SERVFAIL, and a timeout all mean "no IP address". Why does a resolver treat one as an answer and the others as reasons to try again?
Outcome
Outcome
By the end of this session, the learner should be able to:
- classify one server attempt into Answered, NXDOMAIN, SERVFAIL, REFUSED, or Timeout
- explain why NXDOMAIN stops the fallback while SERVFAIL does not
- describe server fallback as an ordered loop with an early exit
- explain what the resolver reports when every server fails differently
Read Order
Read Order
- Read
ServerAttempt - Read
classify_attempt() - Read
is_final() - Read
try_servers() - Run
examples/dns/session_07_walkthrough.py - For each scenario, predict
answered_byandtriedbefore looking
Read It Like Code
Read It Like Code
ServerAttempt(
server,
timed_out,
rcode,
records,
)Fields That Matter
Fields That Matter
| Field | Why it matters |
|---|---|
timed_out | Silence is not an rcode. It has to be modeled as its own field, checked first. |
rcode | The server's verdict when it did respond. |
records | Only meaningful when the attempt is classified as Answered. |
server | Identity matters: the result reports who answered and who was tried. |
Decision Flow
Decision Flow
classify one attempt: timed_out -> Timeout rcode NXDOMAIN -> NameError rcode SERVFAIL -> ServerFailure rcode REFUSED -> Refused otherwise -> Answered is it final? Answered, NameError -> yes, stop the fallback everything else -> no, try the next server
Reading Lens
Reading Lens
The important move in this session is to stop reading every failed lookup as "DNS is down" and start asking:
- is this verdict about the name or about the server?
- which attempt in the list produced the final outcome?
- how many servers were tried before the loop stopped?
Toy Model Boundary
Toy Model Boundary
Real resolvers add retry timers, exponential backoff, and per-server reputation. This lesson keeps the attempts as a pre-built tuple so the only logic under study is classification and the stop/continue decision.
The attempts tuple plays the role of "what each server would say if asked" — the loop decides how far down the list the resolver actually gets.
Code Landmarks
Code Landmarks
classify_attempt()
Timeout is checked before rcode, because a timed-out attempt has no rcode worth reading.
is_final()
The single most important line in the file. NXDOMAIN is grouped with Answered, not with the failures — an authoritative "this name does not exist" is information about the name, and asking another server cannot change it.
try_servers()
An ordered loop with an early exit. Note what happens when nothing is final: the result carries the last non-final kind and an empty answered_by.
Failure Questions
Failure Questions
Use the source file to answer these:
- Why does the NXDOMAIN scenario stop after one attempt even though the second server has records?
- Which classification comes back for an attempt with both
timed_out=Trueandrcode="SERVFAIL", and why? - When every server fails, which kind does the result carry?
- Why is REFUSED retryable here while NXDOMAIN is not?
- What does an empty
answered_bytell you that the kind alone does not?
Walkthrough
Walkthrough
Run this:
PYTHONPATH=src python3 examples/dns/session_07_walkthrough.py
The walkthrough runs five server lists and prints who answered and how many servers were tried.
Done When
Done When
The learner can say all of the following without looking at notes:
- "NXDOMAIN is an answer about the name. SERVFAIL and timeout are complaints about a server."
- "Fallback is an ordered loop that stops on the first final outcome."
- "Retrying a different server can fix a server problem, never a name problem."
References
References
- RFC 1035 Section 4.1.1 (RCODE)
- RFC 2308 Section 2.1 (name errors)
- RFC 4697 (observed resolver misbehavior, including retry storms)
Continue