DNS Stateful Operations
updates
Extracted elements (31)
DSO uses a TLV-based format appended to a bare DNS header rather than an RR-based format, specifically to avoid conflating stateful operations with normal DNS or EDNS(0) functionality and to avoid the middlebox-incompatibility problems that have affected EDNS(0). The format is more explicit and compact, with no redundant or overloaded fields.
Each DSO operation is communicated in its own separate DNS message rather than packing multiple operations into a single message. Because DSO always uses connection-oriented transport, TCP handles batching; this yields per-operation success/failure semantics via a clear RCODE in each response, avoiding the ambiguity of EDNS(0) where there is no mechanism to acknowledge individual options within a combined OPT RR.
Two separate timers—inactivity timeout and keepalive interval—serve distinct purposes: the inactivity timeout governs speculative idle sessions, while the keepalive interval ensures sufficient traffic to maintain NAT/firewall state. A session with an active long-lived operation (e.g., a Push Notification subscription) is not considered inactive even with no messages flowing, which is why the two timers are independent.
DNS Stateful Operations are only applicable on DNS-over-TCP and DNS-over-TLS. DNS-over-UDP is excluded because it does not provide in-order delivery or session semantics. DNS-over-HTTP is excluded because it has its own session management mechanism incompatible with DSO.
There is no mechanism for upgrading from DNS-over-TCP to DNS-over-TLS mid-connection. A connection is either DNS-over-TCP or DNS-over-TLS from the start; once a DSO session is established over a plaintext TCP connection, it remains plaintext for its entire lifetime.
A DSO Retry Delay message MUST NOT be initiated by a client. If a server receives a DSO Retry Delay message from a client, this is a fatal error and the server MUST forcibly abort the connection immediately.
A responder that receives a DSO request or unidirectional message with a malformed MESSAGE ID (nonzero when it must be zero, or zero when it must be nonzero per the Primary TLV specification) MUST treat it as a fatal error and forcibly abort the connection immediately.
A server MUST NOT send a DSO Keepalive message with a keepalive interval value less than ten seconds. If a client receives a DSO Keepalive specifying a keepalive interval less than ten seconds, this is a fatal error and the client MUST forcibly abort the connection immediately.
After sending a DSO Retry Delay message, the server MUST NOT send any further messages on that DSO Session and MUST silently ignore any incoming requests. The server SHOULD allow the client five seconds to close gracefully before forcibly aborting.
If a DSO response is received for an operation where the specification requires a particular TLV or TLVs that are absent, or where more than the specified number of instances of a TLV are present, this is a fatal error and the recipient MUST forcibly abort the connection immediately.
If a DSO unidirectional message (QR=0, MESSAGE ID=0) is received containing an unrecognized Primary TLV, this is a fatal error and the recipient MUST forcibly abort the connection immediately. A DSOTYPENI error MUST NOT be returned in this case.
Servers MUST NOT enable TCP Fast Open (TFO) when listening for connections that do not require TLS, because the early-data restrictions for DSO (which prohibit certain Primary TLVs and all unidirectional messages from appearing in early data) cannot be enforced on non-TLS connections.
The edns-tcp-keepalive EDNS(0) Option MUST NOT be used in any messages sent on a DSO Session. If a message on a DSO Session contains this option, it is a fatal error and the recipient MUST forcibly abort the connection immediately, because DSO's Keepalive TLV supersedes this option.
The four DNS header count fields (QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT) MUST be set to zero on transmission in DSO messages. If a DSO message is received where any of the count fields are not zero, a FORMERR MUST be returned.
Unless it is known in advance by external means that a client supports DSO, a server MUST NOT initiate DSO request messages or DSO unidirectional messages until a DSO Session has been mutually established by at least one successful DSO request/response exchange initiated by the client.
Upon receipt of a DSO Retry Delay message, the client MUST note the reconnect delay and immediately close the connection gracefully. When reconnecting to the same service instance, the client MUST respect the indicated delay before attempting to reconnect; clients SHOULD NOT randomize the delay, as the server handles jitter.
When the inactivity timeout elapses without any active operation on the DSO Session, the client MUST begin closing the idle connection gracefully. The server MUST consider the client delinquent and MUST forcibly abort the session if the idle period exceeds five seconds or twice the current inactivity timeout value, whichever is greater.
DSO defines RCODE 11 (DSOTYPENI, 'DSO-TYPE Not Implemented') to indicate that the server supports DSO but does not implement the DSO-TYPE of the Primary TLV in the request. This is distinct from NOTIMP (4), which signals that the server does not implement DSO at all. A server MUST NOT return DSOTYPENI for a DSO Keepalive request, as the Keepalive TLV is mandatory.
DSO uses OPCODE value 6, a new DNS OPCODE for DNS Stateful Operations. DSO messages use the standard 12-byte DNS header but omit all four resource-record sections; the DSO-specific payload is TLV-encoded data appended after the header.
The Encryption Padding TLV carries opaque padding bytes to obscure DSO message lengths for traffic analysis resistance over TLS, replacing the EDNS(0) Padding Option which cannot appear in DSO messages. It is usable as a Primary TLV or an Additional TLV in both requests and responses.
The Keepalive TLV carries two 32-bit unsigned millisecond values: the Inactivity Timeout (maximum idle time before a client must close the session) and the Keepalive Interval (maximum time between any DNS messages before the client must send a keepalive). The default value for both on a new DSO session is 15 seconds. The special value 0xFFFFFFFF represents infinity.
The MESSAGE ID field is nonzero in DSO request messages (which require a response) and MUST be zero in DSO unidirectional messages (which must not elicit any response). Whether a message is a request or unidirectional is determined solely by the specification of the Primary TLV, not by the MESSAGE ID value. MESSAGE ID namespaces are independent per direction, so client and server may simultaneously use the same 16-bit value.
The Retry Delay TLV carries a 32-bit reconnect delay value (in milliseconds) and is used by the server to initiate graceful termination of a DSO session. The RCODE in the accompanying DNS header indicates the reason for termination. It may also appear as a Response Additional TLV alongside another DSO response. Clients MUST NOT initiate this TLV.
This document creates the IANA 'DSO Type Codes' registry for 16-bit DSO-TYPE values used in DSO TLVs. Initial entries include the three mandatory base TLVs: Keepalive (0x0001), Retry Delay (0x0002), and Encryption Padding (0x0003). New entries require IETF Review.
This document registers DNS OPCODE value 6 as 'DNS Stateful Operations (DSO)' in the IANA DNS OpCodes registry.
This document registers RCODE value 11 as 'DSOTYPENI' ('DSO-TYPE Not Implemented') in the IANA DNS RCODEs registry, for use in DSO response messages when the server supports DSO but does not implement the requested DSO-TYPE.
DSO provides a mechanism for responder-initiated cancellation of long-lived operations by sending a new DSO response message with the original operation's MESSAGE ID and a nonzero RCODE indicating the reason, allowing selective cleanup without terminating the entire session.
TLS 1.3 zero round-trip (0-RTT) early data may be used to begin a DSO Session, but only for DSO-TYPEs explicitly permitted in early data; unidirectional messages are never permitted as early data unless an implicit DSO Session exists. If a server receives an early-data DSO message with an impermissible Primary TLV, it MUST forcibly abort the connection.
The DSO session lifecycle proceeds through named states: Connection Establishment, Connected but Sessionless, DSO Session Establishment in Progress (client sent request, awaiting response for up to 30 seconds), DSO Session Establishment Timeout (30 seconds elapsed with no response — client forcibly aborts), DSO Session Establishment Failed (nonzero RCODE received), DSO Session Established (NOERROR received), Server Shutdown, Client Shutdown, Reconnect, Forcibly Abort, and Abort Reconnect Wait.
A DSO message begins with the standard 12-byte DNS header with OPCODE=6; the four count fields (QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT) are all set to zero and their corresponding sections are absent. DSO Data expressed in TLV syntax immediately follows the header. As in DNS-over-TCP, a 16-bit length prefix frames each message.
Each DSO TLV encodes a 16-bit unsigned DSO-TYPE (network byte order) identifying the operation type, a 16-bit unsigned DSO-LENGTH giving the byte length of DSO-DATA, and variable-length DSO-DATA treated as opaque by the generic DSO machinery and interpreted only by code implementing that DSO-TYPE.