From 131787979c700becaf6ec24a810658d1313587cc Mon Sep 17 00:00:00 2001 From: James Coglan Date: Fri, 28 Jun 2024 13:41:31 +0100 Subject: [PATCH 1/2] resolved: allow the full TTL to be used by OPT records Whereas RFC 1035 says the TTL field takes the "positive values of a signed 32 bit number", and RFC 2181 says "Implementations should treat TTL values received with the most significant bit set as if the entire value received was zero,", the dns_packet_read_rr() function sets rr->ttl to zero if the MSB is set. However, EDNS(0) as specified in RFC 6891 repurposes the TTL field's 4 octets to store other information, c.f.: +0 (MSB) +1 (LSB) +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 0: | EXTENDED-RCODE | VERSION | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 2: | DO| Z | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ The first octet extends the usual 4-bit RCODE from the packet header by providing an additional 8 bits of space, extending the RCODE to 12 bits. But, our handling of the TTL field means that the high bit in the first octet is not actually usable, since setting it will mean these 4 octets are replaced with 0. This may have the effect of making us believe a server does not support DNSSEC when it actually set the DO bit in its OPT record. Here we change things so that the TTL is only set to zero for record types other than OPT. --- src/resolve/resolved-dns-packet.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 67a83269e2..c32a1a9a67 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -1804,9 +1804,9 @@ int dns_packet_read_rr( if (r < 0) return r; - /* RFC 2181, Section 8, suggests to - * treat a TTL with the MSB set as a zero TTL. */ - if (rr->ttl & UINT32_C(0x80000000)) + /* RFC 2181, Section 8, suggests to treat a TTL with the MSB set as a zero TTL. We avoid doing this + * for OPT records so that all 8 bits of the extended RCODE may be used .*/ + if (key->type != DNS_TYPE_OPT && rr->ttl & UINT32_C(0x80000000)) rr->ttl = 0; r = dns_packet_read_uint16(p, &rdlength, NULL); From c40f3714c9a4d1f2bcd308625c9c835892e3d41c Mon Sep 17 00:00:00 2001 From: James Coglan Date: Fri, 28 Jun 2024 13:58:22 +0100 Subject: [PATCH 2/2] resolved: correct parsing of OPT extended RCODEs The DNS_PACKET_RCODE() function works out the full RCODE by taking the first octet from the OPT record TTL field and bitwise-OR-ing this with the basic RCODE from the packet header. This results in RCODE values being lower than they should be. For example, if the first TTL octet is 0x7a and the basic RCODE is 3, this function currently returns `0x7a | 3` = 123, rather than 0x7a3 = 1955. The first TTL octet is supposed to form the upper 8 bits of a 12-bit value, whereas the current implementation constraints the value to 8 bits and results in mis-interpreted RCODEs. This fixes things by shifting the TTL 20 places instead of 24 and masking off the low nibble that comes from the upper bits of the version octet. Note that dns_packet_append_opt() correctly converts the input RCODE into the high octet of the OPT TTL field; this problem only affects parsing of incoming packets. --- src/resolve/resolved-dns-packet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 8f5a08e357..a2e25231df 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -117,7 +117,7 @@ static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) { uint16_t rcode; if (p->opt) - rcode = (uint16_t) (p->opt->ttl >> 24); + rcode = (uint16_t) ((p->opt->ttl >> 20) & 0xFF0); else rcode = 0;