6.1 Receiving Queries
The stub listener's address, its loopback-only rule and its rendering are PSPU §6.8. This article covers how resolvd reads queries; the replies are §6.2.
6.1.1 UDP #
When the UDP socket is readable, at most 64 datagrams are read per event-loop iteration, including malformed or dropped datagrams. For each:
- A datagram whose source address is not a loopback address — anything
outside
127.0.0.0/8— is dropped without a reply. - It is checked as a query (below). A refused query gets its error reply, or nothing.
- A valid query is admitted if fewer than 16 UDP questions from its
source address and port, and fewer than 256 UDP questions in total,
are waiting for engine answers. Otherwise it gets
SERVFAIL, with no upstream transaction, and increments bothqueriesandrefused. This admission check precedes synthetic and cache lookup. - An admitted query becomes an engine question (§4.1). Its slot is released when the engine completes it, including timeout completion.
The endpoint is an accounting key, not an authenticated process identity. Changing source ports can consume the aggregate UDP allowance but cannot consume the separate native or TCP admission allowances. These bounds limit retained work; they do not promise fairness between local processes.
6.1.1.1 The UDP read buffer #
Each datagram is read into a 4 096-byte buffer; a longer datagram is truncated by the read to its first 4 096 bytes.
6.1.1.2 A UDP query longer than the buffer #
A query longer than 4 096 bytes therefore arrives cut short. When the
cut falls inside the DNS message, the message no longer decodes and,
being at least 12 bytes long, is answered FORMERR (below). Bytes after
the end of the message are ignored by decoding, so a query whose message
fits in 4 096 bytes and is only padded beyond them is answered as usual.
6.1.1.3 Where UDP replies go #
Replies are sent to the source address and port the query came from.
6.1.2 TCP #
When the TCP listener is readable, at most 64 waiting connections are accepted per event-loop iteration. A connection is closed at once, without a reply:
- when 256 stub TCP connections are still sending queries or waiting for engine answers;
- when its source is not a loopback address.
A connection that cannot be made nonblocking is closed the same way.
6.1.2.1 Reading a TCP query #
Each held connection is read as it becomes readable, everything available each time, in 4 096-byte chunks. A message is a two-byte big-endian length and that many bytes.
- When more than 65 537 bytes are buffered, the connection is closed without a reply.
- When a whole message has arrived, it is checked as a query, and the connection leaves the set of connections still sending, but continues to occupy its admission slot while the engine works.
- A connection carries one query. Anything sent after the first message is ignored, and the connection is closed once the answer, or the error reply, has been written.
- A connection that closes, or fails to read, before its message is whole is closed without a reply.
- A client that sends a complete query and then shuts down its writing side is still answered. This also applies when the read finds end of file immediately after the buffered query. The connection closes after its one reply. A partial frame followed by end of file is dropped immediately, without waiting for the delivery timeout.
- A connection that has not delivered a whole message ten seconds after it was accepted is closed without a reply.
- Once its query has arrived the ten-second bound no longer applies; the connection is held until the engine answers or a definite hangup/error abandons its question (§4.1). Reply writers use a separate bounded queue (§2.5).
6.1.3 Checking a query #
6.1.3.1 The NOTIMP and question-count FORMERR replies #
These two replies are built from the query:
- the header copies the query's ID, opcode,
RDandCDbits, and setsQRandRA; every other bit is clear; - the question section is the query's, whatever it holds;
- when the query carried an
OPTrecord, the reply carries one whose payload size is the size the query advertised, withDOclear and no options; without one, the reply has none.
6.1.4 What an accepted query contributes #
- Only
INquestions are accepted. Their decoded name and type reach the engine; unsupported classes are rejected at the stub door over both UDP and TCP. RD,CD, theDObit and any EDNS options are not looked at.- Stub queries always consult the cache (§4.5).
6.1.4.1 Wire names #
The decoded name reaches the engine directly, without conversion to presentation text and reparsing. Label boundaries and bytes, including embedded dots, backslashes, NUL and other binary bytes, are preserved. The usual upstream ASCII case randomization still applies (§4.3).
Names are subject to DNS wire limits, not the length of a displayed escape sequence. A valid 63-byte binary label or a valid name whose display form would exceed 255 bytes can therefore be resolved normally.
Search expansion (§4.1) uses the number of wire labels: a single label containing a literal dot remains a single-label name.
6.1.5 Which stub queries are counted #
Every valid query counts in queries (§4.10), including a UDP query
refused by the pending budgets (which also increments refused). Queries
rejected by syntax, opcode, question count or class checks do not count.