DNS Recursive Resolution, Packet by Packet

This walkthrough uses the capture from Chris Greer's video How DNS Works Under the Hood (Packet by Packet), Part 3 of his DNS series. We decoded it with VisualEther, which turns a Wireshark PCAP into a sequence diagram with a plain-English caption on every message. Chris hasn't reviewed or endorsed this article; any annotation errors are ours.

Chris's capture, dns_full_recursion.pcapng, is available from his GitHub repository. Download it there to follow along in Wireshark.

💡 Read the walkthrough and the diagram side by side. Open the interactive diagram (or PDF) in a split-screen window so the captions stay in view as you read. In Edge, Chrome, or Firefox, right-click the link and pick your browser's split-screen option (Edge labels it "Open link in split screen window"). On macOS, prefer Chrome or Firefox — Safari has no in-browser split-screen. Otherwise, open the diagram in a new tab and snap the two windows side by side.

Overview

When a laptop looks up a name, it sends one question to its DNS resolver and gets one answer back. Everything in between happens where the laptop can't see it: the resolver walks the DNS tree from the root down, asking a different server at each level. The capture was taken at the resolver, so both sides are on the wire: the client's two packets over IPv4 and the resolver's 31 upstream frames over IPv6.

The client asks for the address of b2b.infoblox.com. The whole lookup takes 159 ms and involves three servers beyond the resolver:

sequenceDiagram accTitle: A full DNS recursive lookup from client to authoritative server accDescr: The client sends a recursive query for b2b.infoblox.com to its resolver. The resolver asks G-root, which returns a referral to the com servers. The resolver asks g.gtld-servers.net, which returns a referral to the infoblox.com servers. The resolver asks ns5.infoblox.com, which returns the authoritative answer 8.39.143.138. The resolver then answers the client. participant C as Client participant R as Recursive resolver participant Root as G-root participant Com as g.gtld-servers.net participant Auth as ns5.infoblox.com C->>R: [1] A b2b.infoblox.com? RD=1 R->>Root: [2] A b2b.infoblox.com? RD=0 Root-->>R: [19] referral to com (over TCP) R->>Com: [21] A b2b.infoblox.com? RD=0 Com-->>R: [23] referral to infoblox.com R->>Auth: [24] A b2b.infoblox.com? RD=0 Auth-->>R: [30] A 8.39.143.138, AA=1 R-->>C: [31] A 8.39.143.138, RA=1
The lookup at a glance. The client sends one recursive query (RD=1). The resolver then asks the root, the com servers, and the infoblox.com servers in turn, each with an iterative query (RD=0). The root's first two replies were truncated, so the resolver repeated both questions over TCP (frames 6 to 20, not shown). Frame numbers are in brackets.

The walkthrough follows the capture in six phases:

  1. The client asks once (frame 1). A recursive query: "find this for me."
  2. A cold resolver starts at the root (frames 2–5). The resolver primes its list of root servers and asks for the name, but both replies come back truncated.
  3. Retry over TCP (frames 6–20). The resolver repeats both questions over TCP and gets the root server list and a referral to com.
  4. The com referral (frames 21–23). A com server refers the resolver to the infoblox.com servers, with their addresses.
  5. The authoritative answer (frames 24 and 30). An infoblox.com server returns the address.
  6. The client gets its answer (frame 31). The resolver passes the address back.

The remaining frames close the two TCP connections to the root, including one retransmitted FIN.

A primer on recursive resolution

A few ideas make the frames easy to read:

flowchart TB accTitle: The DNS tree walked by this lookup accDescr: The root zone delegates com to the gtld-servers.net servers. The com zone delegates infoblox.com to ns1, ns5, ns6, and ns7 dot infoblox.com. The infoblox.com zone holds the A record for b2b.infoblox.com. Root["Root zone ( . )<br/>13 servers, a–m.root-servers.net"] Com["com<br/>13 servers, a–m.gtld-servers.net"] Zone["infoblox.com<br/>ns1, ns5, ns6, ns7.infoblox.com"] Host["b2b.infoblox.com<br/>A 8.39.143.138"] Root -->|"referral, frame 19"| Com Com -->|"referral, frame 23"| Zone Zone -->|"answer, frame 30"| Host
The part of the DNS tree this lookup walks. Each arrow is a delegation, published as a referral by the parent zone. The resolver learns each level's servers from the referral one level up.

Phase 1 — The client asks once (frame 1)

The client (192.168.80.4) asks the resolver (192.168.0.140) for the A record, the IPv4 address, of b2b.infoblox.com. The query sets RD=1: do the whole lookup for me.

It also sets AD=1, which asks the resolver to report whether it validated the answer with DNSSEC (RFC 6840). It includes an EDNS client cookie (RFC 7873) and advertises a 1,232-byte UDP buffer. It leaves the DO bit clear, so it doesn't want DNSSEC records in the reply.

The client sends nothing else until the answer arrives in frame 31.

Phase 2 — A cold resolver starts at the root (frames 2–5)

The resolver behaves like one with an empty cache: it starts at the root and primes. Both replies come back truncated.

sequenceDiagram accTitle: Truncated replies from the root accDescr: The resolver sends G-root an A query for b2b.infoblox.com and an NS query for the root zone, both over UDP with a 512-byte EDNS limit and the DO bit set. The root answers both with the truncated bit set and no answer or authority records, because the signed answers are larger than 512 bytes. participant R as Recursive resolver participant Root as G-root R->>Root: [2] A b2b.infoblox.com? (UDP, 512 B, DO=1) R->>Root: [3] NS for root? priming (UDP, 512 B, DO=1) Root-->>R: [4] TC=1, no answer Root-->>R: [5] TC=1, no answer
Frames 2 to 5. The resolver sends its real query and a priming query to G-root, each advertising a 512-byte UDP limit with DO=1. Both signed answers are larger than 512 bytes, so the root returns truncated replies (TC=1) with no answer. The full answers were 1,109 and 1,179 bytes.

Frame 2 — the real question, sent to a root server. 0.6 ms after the client's query, the resolver asks 2001:500:12::d0d for b2b.infoblox.com. Frame 15 later proves this is g.root-servers.net. Nothing in the capture supplies that address before this query, so it comes from the resolver's built-in root hints: the list of root server addresses every resolver ships with.

Four details matter here:

Frame 3 — priming. 0.1 ms later, the resolver sends the same root server a priming query (RFC 8109): "who are the root servers?", an NS query for the root zone. Priming replaces the built-in hints with the live, signed list. The resolver doesn't wait for it; the real query is already on its way.

Frames 4 and 5 — both replies truncated. 27 ms later, the root answers both queries with TC=1 and empty answer and authority sections. Each truncated reply is just the header, the question, and the EDNS record: 28 bytes for the priming reply, the same size as its query. The full answers, seen later over TCP, are 1,109 and 1,179 bytes.

Here is frame 5 as Wireshark decodes it. Under Flags, the Truncated bit is set, and the record counts show one question, no answers, no authority records, and one additional record: the EDNS record.

⚠️ [00005] Frame 5 DNS Response TC=1 (truncated) 2025-11-21T18:09:57.164776203Z 🌐 Root server (G-root) → 🔁 Recursive resolver
frame : Frame 5: Packet, 109 bytes on wire (872 bits), 109 bytes captured (872 bits) on interface unknown, id 0
Section number : 1
Interface id : 0 (unknown)
Interface name : unknown
Encapsulation type : Linux cooked-mode capture v1 (25)
Arrival Time : Nov 21, 2025 10:09:57.164776203 Pacific Standard Time
UTC Arrival Time : Nov 21, 2025 18:09:57.164776203 UTC
Epoch Arrival Time : 1763748597.164776203
Time shift for this packet : 0.000000000 seconds
Time delta from previous captured frame : 303 nanoseconds
Time delta from previous displayed frame : 303 nanoseconds
Time since reference or first frame : 27.733450 milliseconds
Frame Number : 5
Frame Length : 109 bytes (872 bits)
Capture Length : 109 bytes (872 bits)
Frame is marked : False
Frame is ignored : False
Protocols in frame : sll:ethertype:ipv6:udp:dns
Character encoding : ASCII (0)
sll : Linux cooked capture v1
Packet type : Unicast to us (0)
Link-layer address type : Ethernet (1)
Link-layer address length : 6
Source : 00:8e:73:21:04:50
Unused : 0000
Protocol : IPv6 (0x86dd)
ipv6 : Internet Protocol Version 6, Src: 2001:500:12::d0d, Dst: 2001:470:e49c:1000::140
0110 .... = Version : 6
0110 .... = Version : 6 [This field makes the filter match on "ip.version == 6" possible]
.... 0000 0000 .... .... .... .... .... = Traffic Class : 0x00 (DSCP: CS0, ECN: Not-ECT)
.... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint : Default (0)
.... .... ..00 .... .... .... .... .... = Explicit Congestion Notification : Not ECN-Capable Transport (0)
.... 0000 0000 0000 0000 0000 = Flow Label : 0x00000
Payload Length : 53
Next Header : UDP (17)
Hop Limit : 53
Source Address : 2001:500:12::d0d
Address Space : Global Unicast
Source or Destination Address : 2001:500:12::d0d
Address Space : Global Unicast
Source Host : 2001:500:12::d0d
Source or Destination Host : 2001:500:12::d0d
Destination Address : 2001:470:e49c:1000::140
Address Space : Global Unicast
Source or Destination Address : 2001:470:e49c:1000::140
Address Space : Global Unicast
Destination Host : 2001:470:e49c:1000::140
Source or Destination Host : 2001:470:e49c:1000::140
Stream index : 0
udp : User Datagram Protocol, Src Port: 53, Dst Port: 51976
Source Port : 53
Destination Port : 51976
Source or Destination Port : 53
Source or Destination Port : 51976
Length : 53
Checksum : 0x3dcb [unverified]
Checksum Status : Unverified
Stream index : 1
Stream Packet Number : 2
Timestamps
Time since first frame : 27.167609 milliseconds
Time since previous frame : 27.167609 milliseconds
UDP payload (45 bytes)
dns : Domain Name System (response)
Transaction ID : 0x5d9b
Flags : 0x8210 Standard query response, No error
1... .... .... .... = Response : Message is a response
.000 0... .... .... = Opcode : Standard query (0)
.... .0.. .... .... = Authoritative : Server is not an authority for domain
.... ..1. .... .... = Truncated : Message is truncated
.... ...0 .... .... = Recursion desired : Don't do query recursively
.... .... 0... .... = Recursion available : Server can't do recursive queries
.... .... .0.. .... = Z : reserved (0)
.... .... ..0. .... = Answer authenticated : Answer/authority portion was not authenticated by the server
.... .... ...1 .... = Non-authenticated data : Acceptable
.... .... .... 0000 = Reply code : No error (0)
Questions : 1
Answer RRs : 0
Authority RRs : 0
Additional RRs : 1
Queries
b2b.infoblox.com : type A, class IN
Name : b2b.infoblox.com
Name Length : 16
Label Count : 3
Type : A (1) (Host Address)
Class : IN (0x0001)
Additional records
<Root> : type OPT
Name : <Root>
Type : OPT (41)
UDP payload size : 1232
Higher bits in extended RCODE : 0x00
EDNS0 version : 0
Z : 0x8000
1... .... .... .... = DO bit : Accepts DNSSEC security RRs
.000 0000 0000 0000 = Reserved : 0x0000
Data length : 0
Request In : 2
Time : 27.167609 milliseconds
Rendered from a Wireshark PCAP by VisualEther.

Frame 5, the root's truncated reply to the A query in frame 2. Flags → Truncated: Message is truncated. The counts read Answer RRs 0, Authority RRs 0, Additional RRs 1: only the question and the EDNS record came back.

Two header bits in these replies are worth checking. Frame 4 still has AA=1, because the root is authoritative for its own server list. Frame 5 copies back the CD (checking disabled) bit from frame 2, as RFC 4035 requires.

The root did nothing wrong: with the DNSSEC records the resolver asked for, neither answer fits in 512 bytes. Both would have fit in the 1,232-byte buffer the client itself advertised in frame 1.

Phase 3 — Retry over TCP (frames 6–20)

A resolver that gets TC=1 must ask again over TCP. RFC 7766 requires every general-purpose DNS implementation to support TCP, and truncated replies are a main reason why.

sequenceDiagram accTitle: Retrying a truncated DNS query over TCP accDescr: The resolver opens a TCP connection to G-root on port 53 with a SYN, receives a SYN-ACK, and completes the handshake with an ACK. It then sends the A query for b2b.infoblox.com over TCP. The root returns the full 1,179-byte referral to the com servers, and the resolver closes the connection. participant R as Recursive resolver participant Root as G-root R->>Root: [7] SYN to port 53 Root-->>R: [11] SYN-ACK R->>Root: [12] ACK R->>Root: [13] A b2b.infoblox.com? (TCP) Root-->>R: [19] referral to com, 1,179 bytes R->>Root: [22] FIN
Frames 6 to 20, one of the two connections shown. TCP costs a handshake round trip before the query. Over TCP, each DNS message starts with a 2-byte length field, and the UDP size does not limit the reply.

Frames 6–12 — two connections. 0.4 ms after the truncated replies, the resolver opens two TCP connections to the root on port 53: source port 44653 for the priming query and 43879 for the real one. RFC 7766 would let it send both queries on one connection; this resolver opens one per query.

The resolver's SYN offers an MSS of 1,220 bytes: 1,280 minus 40 bytes of IPv6 header and 20 of TCP. That is consistent with a 1,280-byte MTU, the IPv6 minimum, on the resolver's path. The root's SYN-ACK arrives 25.9 ms later. The handshake costs a full round trip before any DNS can flow.

Frames 10 and 13 — the same questions, over TCP. Each query gets a new ID. Over TCP, every DNS message is preceded by a 2-byte length field (RFC 1035), so the 28-byte priming query travels as 30 bytes. The EDNS UDP size now reads 1,220, but it only limits UDP replies.

Frame 15 — the root server list. The full priming answer, 1,109 bytes:

This is where the capture proves the resolver has been talking to G-root: the AAAA record for g.root-servers.net is 2001:500:12::d0d.

Frame 19 — the referral to com. The root doesn't know b2b.infoblox.com. It has no answer (AA=0, zero answer records), but sends a pointer instead:

The DS record (key tag 19718, algorithm 13, SHA-256 digest) is the root vouching for the signing key of com. The RRSIG covers only the DS record. A parent zone never signs the NS records at a delegation, because the child zone is authoritative for them. The NS records and glue carry a two-day TTL (172,800 seconds); the DS record, one day.

Frames 14, 16, 17, 18, and 20 — TCP bookkeeping. These are acknowledgments and the resolver's first FIN. The resolver closes each connection as soon as it has its answer. Because it closes first, it holds the TCP TIME_WAIT state, not the root server.

Phase 4 — The com referral (frames 21–23)

Frame 21. 0.6 ms after the root's referral, the resolver asks 2001:503:eea3::30, which the glue in frame 19 names as g.gtld-servers.net. It sends the same full-name question, with RD=0, back over UDP with 512 bytes advertised.

Frame 23. The com server replies in 7.9 ms, the fastest exchange of the lookup, with another referral:

Frame 23 decoded, with the four NS records, the DS record, and its signature under Authoritative nameservers, and the glue addresses under Additional records:

➡️ [00023] Frame 23 DNS Referral 2025-11-21T18:09:57.229464768Z 🌐 com TLD server (g-gtld) → 🔁 Recursive resolver
frame : Frame 23: Packet, 448 bytes on wire (3584 bits), 448 bytes captured (3584 bits) on interface unknown, id 0
Section number : 1
Interface id : 0 (unknown)
Interface name : unknown
Encapsulation type : Linux cooked-mode capture v1 (25)
Arrival Time : Nov 21, 2025 10:09:57.229464768 Pacific Standard Time
UTC Arrival Time : Nov 21, 2025 18:09:57.229464768 UTC
Epoch Arrival Time : 1763748597.229464768
Time shift for this packet : 0.000000000 seconds
Time delta from previous captured frame : 7.846729 milliseconds
Time delta from previous displayed frame : 7.846729 milliseconds
Time since reference or first frame : 92.422015 milliseconds
Frame Number : 23
Frame Length : 448 bytes (3584 bits)
Capture Length : 448 bytes (3584 bits)
Frame is marked : False
Frame is ignored : False
Protocols in frame : sll:ethertype:ipv6:udp:dns
Character encoding : ASCII (0)
sll : Linux cooked capture v1
Packet type : Unicast to us (0)
Link-layer address type : Ethernet (1)
Link-layer address length : 6
Source : 00:8e:73:21:04:50
Unused : 0000
Protocol : IPv6 (0x86dd)
ipv6 : Internet Protocol Version 6, Src: 2001:503:eea3::30, Dst: 2001:470:e49c:1000::140
0110 .... = Version : 6
0110 .... = Version : 6 [This field makes the filter match on "ip.version == 6" possible]
.... 0000 0000 .... .... .... .... .... = Traffic Class : 0x00 (DSCP: CS0, ECN: Not-ECT)
.... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint : Default (0)
.... .... ..00 .... .... .... .... .... = Explicit Congestion Notification : Not ECN-Capable Transport (0)
.... 0000 0000 0000 0000 0000 = Flow Label : 0x00000
Payload Length : 392
Next Header : UDP (17)
Hop Limit : 57
Source Address : 2001:503:eea3::30
Address Space : Global Unicast
Source or Destination Address : 2001:503:eea3::30
Address Space : Global Unicast
Source Host : 2001:503:eea3::30
Source or Destination Host : 2001:503:eea3::30
Destination Address : 2001:470:e49c:1000::140
Address Space : Global Unicast
Source or Destination Address : 2001:470:e49c:1000::140
Address Space : Global Unicast
Destination Host : 2001:470:e49c:1000::140
Source or Destination Host : 2001:470:e49c:1000::140
Stream index : 1
udp : User Datagram Protocol, Src Port: 53, Dst Port: 41447
Source Port : 53
Destination Port : 41447
Source or Destination Port : 53
Source or Destination Port : 41447
Length : 392
Checksum : 0x5cac [unverified]
Checksum Status : Unverified
Stream index : 3
Stream Packet Number : 2
Timestamps
Time since first frame : 7.920451 milliseconds
Time since previous frame : 7.920451 milliseconds
UDP payload (384 bytes)
dns : Domain Name System (response)
Transaction ID : 0xd425
Flags : 0x8010 Standard query response, No error
1... .... .... .... = Response : Message is a response
.000 0... .... .... = Opcode : Standard query (0)
.... .0.. .... .... = Authoritative : Server is not an authority for domain
.... ..0. .... .... = Truncated : Message is not truncated
.... ...0 .... .... = Recursion desired : Don't do query recursively
.... .... 0... .... = Recursion available : Server can't do recursive queries
.... .... .0.. .... = Z : reserved (0)
.... .... ..0. .... = Answer authenticated : Answer/authority portion was not authenticated by the server
.... .... ...1 .... = Non-authenticated data : Acceptable
.... .... .... 0000 = Reply code : No error (0)
Questions : 1
Answer RRs : 0
Authority RRs : 6
Additional RRs : 7
Queries
b2b.infoblox.com : type A, class IN
Name : b2b.infoblox.com
Name Length : 16
Label Count : 3
Type : A (1) (Host Address)
Class : IN (0x0001)
Authoritative nameservers
infoblox.com : type NS, class IN, ns ns5.infoblox.com
Name : infoblox.com
Type : NS (2) (authoritative Name Server)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 6
Name Server : ns5.infoblox.com
infoblox.com : type NS, class IN, ns ns6.infoblox.com
Name : infoblox.com
Type : NS (2) (authoritative Name Server)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 6
Name Server : ns6.infoblox.com
infoblox.com : type NS, class IN, ns ns7.infoblox.com
Name : infoblox.com
Type : NS (2) (authoritative Name Server)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 6
Name Server : ns7.infoblox.com
infoblox.com : type NS, class IN, ns ns1.infoblox.com
Name : infoblox.com
Type : NS (2) (authoritative Name Server)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 6
Name Server : ns1.infoblox.com
infoblox.com : type DS, class IN
Name : infoblox.com
Type : DS (43) (Delegation Signer)
Class : IN (0x0001)
Time to live : 86400 (1 day)
Data length : 36
Key id : 0x4d05
Algorithm : RSA/SHA-256 (8)
Digest Type : SHA-256 (2)
Digest : 50b74ae39a95cb9f1609d27c331deb76da82bcb308b00f76b3779df9aa463886
infoblox.com : type RRSIG, class IN
Name : infoblox.com
Type : RRSIG (46) (Resource Record Signature)
Class : IN (0x0001)
Time to live : 86400 (1 day)
Data length : 87
Type Covered : DS (43) (Delegation Signer)
Algorithm : ECDSA Curve P-256 with SHA-256 (13)
Labels : 2
Original TTL : 86400 (1 day)
Signature Expiration : Nov 24, 2025 18:33:28.000000000 Pacific Standard Time
Signature Inception : Nov 17, 2025 17:23:28.000000000 Pacific Standard Time
Key Tag : 46539
Signer's name : com
Signature : 26dc23f35a64d0ff1c9bb1c0a14e09f6c54e2211a8ce0af61ced06734ff49e55bad736bfc2a8816d0b295c89ceb77ffcee7daf523df63bbf6094c645d5549b85
Additional records
ns5.infoblox.com : type AAAA, class IN, addr 2600:1f14:3515:8202:da8:7299:3926:a2c1
Name : ns5.infoblox.com
Type : AAAA (28) (IP6 Address)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 16
AAAA Address : 2600:1f14:3515:8202:da8:7299:3926:a2c1
ns5.infoblox.com : type A, class IN, addr 35.163.191.194
Name : ns5.infoblox.com
Type : A (1) (Host Address)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 4
Address : 35.163.191.194
ns6.infoblox.com : type A, class IN, addr 104.40.90.56
Name : ns6.infoblox.com
Type : A (1) (Host Address)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 4
Address : 104.40.90.56
ns7.infoblox.com : type A, class IN, addr 18.209.84.34
Name : ns7.infoblox.com
Type : A (1) (Host Address)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 4
Address : 18.209.84.34
ns7.infoblox.com : type AAAA, class IN, addr 2600:1f18:b5d:c600:e66b:3429:41ff:5da3
Name : ns7.infoblox.com
Type : AAAA (28) (IP6 Address)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 16
AAAA Address : 2600:1f18:b5d:c600:e66b:3429:41ff:5da3
ns1.infoblox.com : type A, class IN, addr 23.96.113.219
Name : ns1.infoblox.com
Type : A (1) (Host Address)
Class : IN (0x0001)
Time to live : 172800 (2 days)
Data length : 4
Address : 23.96.113.219
<Root> : type OPT
Name : <Root>
Type : OPT (41)
UDP payload size : 4096
Higher bits in extended RCODE : 0x00
EDNS0 version : 0
Z : 0x8000
1... .... .... .... = DO bit : Accepts DNSSEC security RRs
.000 0000 0000 0000 = Reserved : 0x0000
Data length : 0
Request In : 21
Time : 7.920451 milliseconds
Rendered from a Wireshark PCAP by VisualEther.

Frame 23, the com server's referral to infoblox.com. Answer RRs is 0, and the Authoritative flag is clear: this server is pointing elsewhere, not answering. Open any record to see its TTL: 172,800 seconds for the NS records and glue, 86,400 for the DS record and its signature.

Here glue is essential, not a convenience. The name servers for infoblox.com are themselves inside infoblox.com. Without their addresses in this referral, the resolver would need to resolve ns5.infoblox.com to reach the server that holds ns5.infoblox.com.

At 384 bytes, this referral fits in 512 bytes. No truncation, no TCP.

Phase 5 — The authoritative answer (frames 24 and 30)

Frame 24. The resolver asks 2600:1f14:3515:8202:da8:7299:3926:a2c1, which the glue in frame 23 names as ns5.infoblox.com. Only ns5 and ns7 came with IPv6 glue, and this resolver used IPv6 for every upstream query.

Frame 30. ns5 answers after 65.6 ms, the slowest single exchange of the lookup, with AA=1: this server owns the zone, so its answer is final.

Phase 6 — The client gets its answer (frame 31)

0.4 ms later, the resolver answers the client. The transaction ID 0x2380 matches frame 1.

From the client's side, the lookup was two packets and 159 ms. The other 31 frames happened behind the resolver.

Loose ends: closing the TCP connections (frames 22, 25–29, 32–33)

While the resolver works on the com and infoblox.com steps, the two root connections close in the background. Connection 43879 closes cleanly: the resolver's FIN (frame 22), the root's ACK and FIN (frames 27 and 28), and the resolver's final ACK (frame 29).

Connection 44653 takes longer. The root's FIN in frame 26 gets no ACK in the capture, so the root retransmits it one second later, in frame 32. The resolver acknowledges the retransmission in frame 33. The capture doesn't show why the first FIN went unacknowledged. It made no difference to the client, which had its answer 950 ms earlier.

Where the time went

StepFramesTime
Root, over UDP (truncated)2–527 ms
Root, retry over TCP6–1956 ms
com server21–238 ms
infoblox.com server24–3066 ms
Total, client query to answer1–31159 ms

The truncation detour costs about 56 ms, more than a third of the lookup: a TCP handshake plus a second query round trip. A resolver advertising 1,232 bytes, the size the client used, would have received both root answers over UDP the first time.

Long delegation TTLs make this cost rare. Once cached, the root and com referrals last for days: the next lookup for another .com name starts at the com servers, and one for another infoblox.com name starts at its own servers.

DNSSEC on the wire, and what's missing

The resolver asked for DNSSEC records (DO=1), and the chain of trust is visible in the replies:

Validation also needs the zone keys: the DNSKEY records for the root, com, and infoblox.com. The capture contains no DNSKEY queries, and the reply to the client leaves AD clear. The signatures ride along, but nothing in this capture shows them being checked.

The capture was recorded in November 2025. The key tags and TTLs above describe the zones at that time.

Takeaways

  1. The client sees almost nothing. It sends one question and gets one answer. Troubleshooting a slow or failed lookup means capturing behind the resolver, as Chris did here.
  2. Referrals and glue drive the walk. Each level answers "not me, ask them," and the glue addresses let the resolver take the next step, especially when a zone's name servers live inside the zone.
  3. The header bits tell you who is speaking. RD=1 from a client, RD=0 upstream, AA=1 from the zone owner, RA=1 from the resolver.
  4. The EDNS buffer size has a real cost. A 512-byte limit with DNSSEC turned two UDP exchanges into two TCP connections and added a third of the lookup time.
  5. Caching hides all this. With a two-day TTL on the com delegation and a 10-second TTL on the address, most repeat lookups start partway down the tree.

Try it on your own capture

Every arrow in this walkthrough was decoded and captioned by VisualEther from the raw Wireshark PCAP, and the interactive diagram above opens each frame's full field tree. Point it at your own DNS, routing, or 5G trace and read it the same way.