Skip to main content

DNS communications

Concept​

A DNS communication is one DNS question and its response: a query sent by a client to a resolver, paired with the answer that came back. It is decoded from the transport sessions captured by the Whisperer, whatever carried it:

TransportTransport sessionFraming
UDP (53)UdpConversation or UdpFlowOne datagram is one DNS message
TCP (53)TcpSession2-byte length prefix, a message split by a parsing pass is carried over
DNS over TLS (853)TcpSession, TLSDecrypted with the TLS keys captured by Gocipher, then framed as TCP
DNS over QUIC (853)UdpFlow, TLSQUIC decrypted with the TLS secrets captured by Gocipher, one DNS exchange per QUIC stream

Each DNS communication is associated with:

  • Source and destination IP address and port
  • The transport (UDP or TCP) and the kind of transport session it came from
  • The question: name, type and class, with the header flags and EDNS
  • The response: rcode, header flags, EDNS, and the answer, authority and additional records
  • The number of times the client resent the same question
  • A template and tags, computed from the question and the response
DNS is metadata only

DNS has no body: the question and the records are the communication. There is no separate content store, and no payload gating: a query name or an answer IP is metadata, like an HTTP URI. A user without payload access still sees DNS communications in full. Access filters apply as for every protocol.

DNS communications appear in the dedicated DNS view - grid, timeline, dashboard, sequence diagram, waterfall, map, statistics and diff - and in the unified All view.

DNS parsing needs the dnsParsing feature flag and the DNS parsing settings of the Whisperer. DNS over UDP also needs UDP sessions to be tracked on the capture side. All are off by default.

Pairing a query with its response​

A response is paired with its query on (client IP, client port, transaction id).

  • On a UdpConversation, the session groups all the client ports talking to one resolver: the client port is read from each datagram, not from the session.
  • A retransmit is not a new question. The same key and the same question while the query is pending increments req.retries, and no second communication is created.
  • A resolver answering both copies of a retransmitted query is recognised: the second answer is absorbed.
  • An answer whose query was not captured still makes a full communication, rebuilt from the question the response echoes. It is flagged stats.orphanResponse.

On DNS over QUIC, the transaction id is always 0 (RFC 9250): the QUIC stream identifies the exchange. The pairing key is then (client IP, client port, stream id), and the stream id is part of the communication @id.

When a query is unanswered​

A query waits server.dns.responseTimeout (PT5S by default) for its response. This delay is measured on the capture timeline, not wall-clock: against the instant up to which the parser knows it has seen every packet.

The parsing pass...Horizon
reached the live end of the sessionnow minus the parsing delay
was cut by the per-pass boundcapture time of the last packet read
ended the sessionnone: everything pending is flushed

A late response seen before that horizon still pairs, even past the timeout. The consequence: an unanswered query on a conversation that is still open is emitted once the session has been parsed past it, or when the session ends.

Global Tab​

The Global tab summarises the exchange: timing, general, question, response, tags and Spider info.

A Pcap button downloads the packets of the communication.

Timing​

The timing diagram shows the client and the resolver, the query and the response scaled by the packet timestamps, the question (A api.example.com) and the response summary (NOERROR (1 answer)).

General​

FieldDescription
FromThe client host and port
Client workloadKubernetes workload of the client, when resolved - see Workload identity
ToThe resolver host and port
Server workloadKubernetes workload of the resolver, when resolved
TransportUDP or TCP, with the kind of transport session: TCP, UDP flow or UDP conv.
RetriesNumber of times the client resent the question. Shown only when not zero.
OrphanShown when the response was captured without its query

Question​

FieldDescription
DateDate the query was sent, user's local time
QuestionName, type and class of the question (api.example.com A IN)
TemplateComputed name of the question, see Templates
OpcodeDNS opcode, usually QUERY
SizeSize of the query

Response​

FieldDescription
RcodeResponse code name and number, colored by status
AnswersNumber of records in the answer section
Min TTLSmallest TTL of the answer records, in seconds
DurationDelay between the query and the response
SizeSize of the response

When no response was captured, the section shows No response.

Tags​

Tags are configured at Whisperer level, on the question (qname, qtype, qclass) or on the response (rcode, answers, min_ttl). A DNS tag is multi-valued: an answer tag holds every matching record. Each tag shows its count and cardinality.

Spider info​

FieldDescription
WhispererName and link to the Whisperer
InstanceInstance of the Whisperer that captured the communication
Tcp sessionLink to the transport session. Titled UDP flow or UDP conversation for UDP
Parsing sessionThe parsing log, when saved
Request parsingParsing status of the request
Response parsingParsing status of the response, MISSING when there is none

Request tab​

The Request tab shows the DNS header and its question.

SectionFields
HeaderTransaction id, opcode, flags (RD, CD), retries
QuestionName, type, class
EDNSUDP payload size, DNSSEC OK (DO) bit, and the EDNS options (code and data)
Client socketThe real client IP and port, read from the packet

On an orphan response, these values are rebuilt from the response.

Response tab​

The Response tab shows the DNS header of the response and its three record sections.

SectionFields
HeaderRcode and its number, flags (AA, TC, RA, AD), EDNS UDP size and DO bit
AnswersOne row per record: name, type, class, TTL, data
AuthoritySame
AdditionalSame
  • Records are decoded with the library CoreDNS is built on: SVCB and HTTPS records, DNSSEC types and EDNS0 options decode properly. An unknown record type is kept as hex.
  • Each section keeps at most 64 records. Beyond that, res.rrTruncated is set, and the counts still give the wire values.
  • A response with the TC bit was truncated on the wire: the client should retry over TCP. That retry is another communication, on another session, and the two are not linked.

Packets tab​

The Packets tab lists the packets the communication was decoded from. You may open them by clicking on the rows.

On DNS over TLS and DNS over QUIC, the packets are encrypted.

Source​

The Source tab shows the DNS communication in JSON format, as available through the API. Below is a sample, abridged:

{
"@id": "...",
"@type": "DnsCom",
"kind": "dns",
"whisperer": "...",
"tcpSession": "...",
"stats": {
"transport": "UDP",
"sessionType": "UdpConversation",
"withTls": false,
"statusCode": 200,
"statusText": "NOERROR",
"duration": 0.0011,
"orphanResponse": false
},
"req": {
"txid": 40211,
"opcode": "QUERY",
"qname": "api.example.com",
"qtype": "A",
"qclass": "IN",
"rd": true,
"retries": 0,
"template": "api.example.com"
},
"res": {
"rcode": "NOERROR",
"rcodeCode": 0,
"aa": false,
"tc": false,
"ra": true,
"answerCount": 1,
"answers": [
{"name": "api.example.com", "type": "A", "class": "IN", "ttl": 30, "data": "10.0.12.7"}
],
"minTtl": 30
}
}
FieldDescription
stats.transportUDP or TCP. DNS over QUIC is UDP with withTls, DNS over TLS is TCP with withTls
stats.sessionTypeTcpSession, UdpFlow or UdpConversation
stats.withTlstrue for DNS over TLS and DNS over QUIC
stats.statusCode200, 500 or 1000, see Statuses
stats.statusTextThe rcode name, or No response
stats.orphanResponsetrue when the response was captured without its query
stats.tags.*Extracted tag values, count and cardinality
req.qnameQueried name, without its trailing dot
req.retriesNumber of times the client resent the question
req.ednsudpSize, do and options of the query
req.clientIp/PortReal client socket, read from the packet
req.templateComputed name of the question, see Templates
res.rcodeResponse code name (NOERROR, NXDOMAIN, SERVFAIL...). Absent when there is no response
res.rcodeCodeResponse code number
res.answerCount, res.authorityCount, res.additionalCountRecord counts, as on the wire
res.answers, res.authority, res.additionalRecords: name, type, class, ttl, data. At most 64 each
res.rrTruncatedtrue when records beyond the 64 per section were dropped
res.minTtlSmallest TTL of the answer records
dnsParsingId of the parsing log document for this communication

Templates​

req.qname has a high cardinality: pod IPs, hashes, UUIDs, reverse lookups. The req.template collapses it:

  1. The first template rule whose pattern matches the name, lower-cased and without its trailing dot.
  2. Otherwise the normalised name: labels that are numbers, UUIDs, long hexadecimal strings or encoded IP addresses become ?. pod-10-1-2-3.ns.pod.cluster.local becomes pod-?.ns.pod.cluster.local, and every reverse lookup becomes ?.in-addr.arpa or ?.ip6.arpa.

Statuses​

DNS uses the same status classes as the other protocols:

stats.statusCodestats.statusTextMeaning
200NOERRORThe resolver answered without error
500the rcode nameAny other rcode (NXDOMAIN, SERVFAIL, REFUSED...), raw value on res.rcode
500IncompleteA response started on TCP or QUIC but was cut off; res holds what was captured
1000No responseNo response was captured: res is absent

1000 is never counted as an error: it is its own class. Filter on res.rcode to tell NXDOMAIN from SERVFAIL.

DNS over QUIC​

DNS over QUIC (DoQ, RFC 9250) is read from the UdpFlow of the QUIC connection, on the ports listed in server.dns.quicPorts (853 by default):

  • The Whisperer reads the client random from the QUIC Initial packets, and the TLS secrets captured by Gocipher are linked to the flow on it. A flow on a QUIC port that never gets a key is skipped. It is never read as plain DNS.
  • QUIC v1 and v2 are decrypted, key updates are followed. 0-RTT early data is not decrypted: a query sent as early data has no communication.
  • On a stream reset by the server, the partial response is dropped and the query is a 1000 No response. So is a query whose stream stopped coming. A response with a hole is a 500 Incomplete.
  • A connection whose Initial packets were not captured cannot be decrypted. Restart the client after attaching the Whisperer.
  • A client that changes address or port starts a new flow, which cannot be decrypted. A flow idle for longer than the QUIC flow timeout (PT120S by default) is split, and its second part cannot be decrypted either.
  • The Whisperer needs client.tlsKeys.track and a Gocipher on the client or the server.

Search-list noise​

The DNS dashboard has a Search-list noise category. With the default Kubernetes ndots:5, a name such as api.example.com is first tried with every search suffix (api.example.com.<ns>.svc.cluster.local...), each an NXDOMAIN, before the real name resolves.

The widgets match each NXDOMAIN name to a successful name it is an expansion of, and report the share of queries that are pure search-list waste, the time spent on them, and the names responsible.

This is a heuristic: it works on the top 2000 NXDOMAIN and NOERROR names of the window, it is not per client, and a name that never resolved successfully in the window cannot be matched.

Export​

The CSV export of DNS communications gives one row per answer record: the record name, type and ttl as meta.name, meta.type and meta.ttl, its data as the payload value. A communication with no answer exports as a single row. Authority and additional records are not exported.

Retention and purge​

DNS communications follow the same Data Storage Policies as the other protocols. They can be removed from Spider with the purge tool, by whisperer and time window.

Limits​

  • mDNS and LLMNR are not paired. DNS over HTTPS is an HTTP exchange (application/dns-message), not a DNS communication.
  • IP fragments and IPv6 extension headers are not reassembled: a fragmented answer shows as no response.
  • A transaction id reused on the same client port can, rarely, pair a late answer with the wrong question.
  • DNS over HTTP/3 is not decoded.

Access from the CLI and AI agents​

DNS communications are available in the spider CLI (spider search|get|stats|aggs|outliers dns, with --qname, --qtype and --rcode) and in the MCP server (type: dns).