The race
Your laptop asks its resolver for www.example.com. The resolver's cache drawer is empty, so it must ask example.com's own name server.
The resolver asks ns1.example.com over UDP, a quick one-shot message with no handshake. Like most resolvers before 2008, it sends every query from the same port, 52144. Only the random 16-bit transaction ID, 0x5c21, acts as a ticket number the answer must quote.
The resolver accepts the first reply that matches: same question, same ID, to that port, from the server's address. UDP has no handshake, so the sender address on a reply proves nothing; it can be faked.
An attacker who cannot see the query sends a burst of forged replies, faking ns1.example.com's address as the sender. The port never changes, so each guesses only the ID, and each claims www.example.com is 198.51.100.66.
One forged reply happens to carry 0x5c21, the right ID, and reaches the known port 52144 before the real answer. It matches everything the resolver checks, so the resolver accepts it.
The real answer, 203.0.113.10, arrives a few milliseconds later. The query has already been answered, so nothing is waiting for it any more and the resolver throws it away.
The damage
The resolver files the false record in its cache drawer for the time to live (TTL) the attacker chose: 86,400 seconds, a whole day. A poisoned cache was the attacker's goal.
Now everyone who uses this resolver gets 198.51.100.66 for www.example.com, straight from the cache. Their browsers connect to the attacker's server instead of the real one.
In 2008 Dan Kaminsky showed a faster way. The attacker asks for made-up names like x1.example.com, so each one is a fresh race the cache cannot stop, and a winning forgery can take over all of example.com.
Odds, and the easy case
With one fixed source port, each forgery had a 1 in 65,536 chance. Since 2008, and in RFC 5452, resolvers pick a random port for every query too: 65,536 IDs × 64,512 ports is about 4.2 billion combinations to guess.
On a shared network it is easier. An attacker that has fooled your laptop with ARP spoofing (pretending to be your router) sits in the middle and receives your query itself, so it reads the ID and port instead of guessing.
It answers at once, faking the resolver's address, with a perfect match, and your laptop believes 198.51.100.66. Next lesson: DNSSEC signatures and encrypted DNS let you reject answers like this.
The messages, step by step
;; step 1/12 · ask-resolver ;; client → resolver UDP 192.168.1.10:50321 → 198.51.100.53:53 id 0x3a7e flags RD question www.example.com. IN A cache: no entry for www.example.com
RFC 5452 §3: a resolver accepts a response only if its question, ID, source address and destination port match a query it is still waiting on; the first one that matches wins. The fix is in the next lesson, "Defence: DNSSEC and encrypted DNS": DNSSEC signatures let a resolver reject forged records; DNS over TLS or HTTPS hides and protects the query on the way.