A PUSH unidirectional message begins with the standard DSO 12-byte header [
RFC 8490], followed by the PUSH Primary TLV. A PUSH message is illustrated in
Figure 3.
In accordance with the definition of DSO unidirectional messages, the MESSAGE ID field
MUST be zero. There is no client response to a PUSH message.
The other header fields
MUST be set as described in the DSO specification [
RFC 8490]. The DNS OPCODE field contains the OPCODE value for DNS Stateful Operations (6). The four count fields must be zero, and the corresponding four sections must be empty (i.e., absent).
The DSO-TYPE is PUSH (0x0041).
The DSO-LENGTH is the length of the DSO-DATA that follows, which specifies the changes being communicated.
The DSO-DATA contains one or more change notifications. A PUSH Message
MUST contain at least one change notification. If a PUSH Message is received that contains no change notifications, this is a fatal error and the client
MUST forcibly abort the connection immediately.
The change notification records are formatted similarly to how DNS Resource Records are conventionally expressed in DNS messages, as illustrated in
Figure 3, and are interpreted as described below.
The TTL field holds an unsigned 32-bit integer [
RFC 2181]. If the TTL is in the range 0 to 2,147,483,647 seconds (0 to 2
31 - 1, or 0x7FFFFFFF), then a new DNS Resource Record with the given name, type, class, and RDATA is added. Type and class
MUST NOT be 255 (ANY). If either type or class are 255 (ANY), this is a fatal error and the client
MUST forcibly abort the connection immediately. A TTL of 0 means that this record should be retained for as long as the subscription is active and should be discarded immediately the moment the subscription is canceled.
If the TTL has the value 0xFFFFFFFF, then the DNS Resource Record with the given name, type, class, and RDATA is removed. Type and class
MUST NOT be 255 (ANY). If either type or class are 255 (ANY), this is a fatal error and the client
MUST forcibly abort the connection immediately.
If the TTL has the value 0xFFFFFFFE, then this is a 'collective' remove notification. For collective remove notifications, RDLEN
MUST be zero, and consequently, the RDATA
MUST be empty. If a change notification is received where TTL = 0xFFFFFFFE and RDLEN is not zero, this is a fatal error and the client
MUST forcibly abort the connection immediately.
There are three types of collective remove notification. For collective remove notifications:
-
If CLASS is not 255 (ANY) and TYPE is not 255 (ANY), then for the given name, this removes all records of the specified type in the specified class.
-
If CLASS is not 255 (ANY) and TYPE is 255 (ANY), then for the given name, this removes all records of all types in the specified class.
-
If CLASS is 255 (ANY), then for the given name, this removes all records of all types in all classes. In this case, TYPE MUST be set to zero on transmission and MUST be silently ignored on reception.
Summary of change notification types:
-
Remove all RRsets from a name in all classes:
TTL = 0xFFFFFFFE, RDLEN = 0, CLASS = 255 (ANY).
-
Remove all RRsets from a name in given class:
TTL = 0xFFFFFFFE, RDLEN = 0, CLASS gives class, TYPE = 255 (ANY).
-
Remove specified RRset from a name in given class:
TTL = 0xFFFFFFFE, RDLEN = 0,
CLASS and TYPE specify the RRset being removed.
-
Remove an individual RR from a name:
TTL = 0xFFFFFFFF,
CLASS, TYPE, RDLEN, and RDATA specify the RR being removed.
-
Add individual RR to a name:
TTL >= 0 and TTL <= 0x7FFFFFFF,
CLASS, TYPE, RDLEN, RDATA, and TTL specify the RR being added.
Note that it is valid for the RDATA of an added or removed DNS Resource Record to be empty (zero length). For example, an Address Prefix List Resource Record [
RFC 3123] may have empty RDATA. Therefore, a change notification with RDLEN = 0 does not automatically indicate a remove notification. If RDLEN = 0 and TTL is in the range 0 to 0x7FFFFFFF, this change notification signals the addition of a record with the given name, type, class, and empty RDATA. If RDLEN = 0 and TTL = 0xFFFFFFFF, this change notification signals the removal specifically of that single record with the given name, type, class, and empty RDATA.
If the TTL is any value other than 0xFFFFFFFF, 0xFFFFFFFE, or a value in the range 0 to 0x7FFFFFFF, then the receiver
SHOULD silently ignore this particular change notification record. The connection is not terminated and other valid change notification records within this PUSH message are processed as usual.
In the case where a single change affects more than one active subscription, only one PUSH message is sent. For example, a PUSH message adding a given record may match both a SUBSCRIBE request with the same TYPE and a different SUBSCRIBE request with TYPE = 255 (ANY). It is not the case that two PUSH messages are sent because the new record matches two active subscriptions.
The server
SHOULD encode change notifications in the most efficient manner possible. For example, when three AAAA records are removed from a given name, and no other AAAA records exist for that name, the server
SHOULD send a "Remove specified RRset from a name in given class" PUSH message, not three separate "Remove an individual RR from a name" PUSH messages. Similarly, when both an SRV and a TXT record are removed from a given name, and no other records of any kind exist for that name in that class, the server
SHOULD send a "Remove all RRsets from a name in given class" PUSH message, not two separate "Remove specified RRset from a name in given class" PUSH messages.
For efficiency, when generating a PUSH message, rather than sending each change notification as a separate DSO message, a server
SHOULD include as many change notifications as it has immediately available to send to that client, even if those change notifications apply to different subscriptions from that client. Conceptually, a PUSH message is a session-level mechanism, not a subscription-level mechanism. Once it has exhausted the list of change notifications immediately available to send to that client, a server
SHOULD then send the PUSH message immediately rather than waiting speculatively to see if additional change notifications become available.
For efficiency, when generating a PUSH message a server
SHOULD use standard DNS name compression, with offsets relative to the beginning of the DNS message [
RFC 1035]. When multiple change notifications in a single PUSH message have the same owner name, this name compression can yield significant savings. Name compression should be performed as specified in
Section 18.14 of
RFC 6762; namely, owner names should always be compressed, and names appearing within RDATA should be compressed for only the RR types listed below:
-
-
NS, CNAME, PTR, DNAME, SOA, MX, AFSDB, RT, KX, RP, PX, SRV, NSEC
Servers may generate PUSH messages up to a maximum DNS message length of 16,382 bytes, counting from the start of the DSO 12-byte header. Including the two-byte length prefix that is used to frame DNS over a byte stream like TLS, this makes a total of 16,384 bytes. Servers
MUST NOT generate PUSH messages larger than this. Where the immediately available change notifications are sufficient to exceed a DNS message length of 16,382 bytes, the change notifications
MUST be communicated in separate PUSH messages of up to 16,382 bytes each. DNS name compression becomes less effective for messages larger than 16,384 bytes, so little efficiency benefit is gained by sending messages larger than this.
If a client receives a PUSH message with a DNS message length larger than 16,382 bytes, this is a fatal error and the client
MUST forcibly abort the connection immediately.
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ \
| MESSAGE ID (MUST BE ZERO) | \
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|QR| OPCODE(6) | Z | RCODE | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| QDCOUNT (MUST BE ZERO) | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ > HEADER
| ANCOUNT (MUST BE ZERO) | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| NSCOUNT (MUST BE ZERO) | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| ARCOUNT (MUST BE ZERO) | /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ /
| DSO-TYPE = PUSH (0x0041) |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| DSO-LENGTH (number of octets in DSO-DATA) |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ \
\ NAME \ \
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| TYPE | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| CLASS | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| TTL | |
| (32-bit unsigned big-endian integer) | > DSO-DATA
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
| RDLEN (16-bit unsigned big-endian integer) | |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
\ RDATA (sized as necessary) \ |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
: NAME, TYPE, CLASS, TTL, RDLEN, RDATA : |
: Repeated As Necessary : /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ /
When processing the records received in a PUSH Message, the receiving client
MUST validate that the records being added or removed correspond with at least one currently active subscription on that session. Specifically, the record name
MUST match the name given in the SUBSCRIBE request, subject to the usual established DNS case-insensitivity for US-ASCII letters. For individual additions and removals, if the TYPE in the SUBSCRIBE request was not ANY (255), then the TYPE of the record must either be CNAME or match the TYPE given in the SUBSCRIBE request, and if the CLASS in the SUBSCRIBE request was not ANY (255), then the CLASS of the record must match the CLASS given in the SUBSCRIBE request. For collective removals, at least one of the records being removed must match an active subscription. If a matching active subscription on that session is not found, then that particular addition/removal record is silently ignored. The processing of other additions and removal records in this message is not affected. The DSO session is not closed. This is to allow for the unavoidable race condition where a client sends an outbound UNSUBSCRIBE while inbound PUSH messages for that subscription from the server are still in flight.
The TTL of an added record is stored by the client. While the subscription is active the TTL is not decremented, because a change to the TTL would produce a new update. For as long as a relevant subscription remains active, the client
SHOULD assume that when a record goes away, the server will notify it of that fact. Consequently, a client does not have to poll to verify that the record is still there. Once a subscription is canceled (individually, or as a result of the DSO session being closed), record aging for records covered by the subscription resumes and records are removed from the local cache when their TTL reaches zero.